1. 程式人生 > 實用技巧 >Boxes in a Line UVA - 12657

Boxes in a Line UVA - 12657

原題連結

除錯題,基本時間都在除錯了,從這題也學到了些東西,如果資料結構的步驟很複雜,可以將其分解為簡單的步驟,這樣可以減少錯誤,比如這道題,所有的操作除了逆轉都可以

換成移除某個結點,再某個結點右邊插入元素,或者更簡單的步驟:將兩個結點相連.

此外,除了這個技巧,當我們需要大規模地修改陣列或者容器時,其中可能有規律,比如這道題的逆轉,並不需要真的逆轉,因為我們只是輸出奇數位的和,我們可以觀察和與逆

轉的關係,但是當我們沒有逆轉時,我們也需要考慮對其他步驟的影響。

此題還有一個坑點,當我們插入元素的時候,如果兩元素相鄰,那麼操作又不同

 1 #include <cstdio>
 2
using namespace std; 3 #define ll long long 4 const int N = 100010; 5 int e[N],l[N],r[N],n,m,op; 6 void insert_r(int x,int k)//把x插到k右邊 7 { 8 r[x] = r[k]; l[x] = k; l[r[k]] = x; r[k] = x; 9 } 10 void remove(int x){ 11 r[l[x]] = r[x]; l[r[x]] = l[x]; 12 } 13 //void link(int x,int y){//將xy相連 14 //
r[x] = y; l[y] = x; 15 //} 16 int main() 17 { 18 // freopen("in.txt","r",stdin); 19 // freopen("out.txt","w",stdout); 20 int kcase = 0; 21 while(scanf("%d%d",&n,&m)==2){ 22 printf("Case %d: ",++kcase); 23 r[0] = n+1; l[n+1] = 0;//哨兵 24 bool isrev = false;ll sum = 0
; 25 for(int i=1;i<=n;i++){ 26 e[i] = i; l[i] = i-1; r[i] = i+1; 27 } 28 r[0] = 1; 29 for(int i=1;i<=m;i++){ 30 scanf("%d",&op); 31 if(op==4) { isrev = !isrev;continue; } 32 int x,y; scanf("%d%d",&x,&y); 33 if(op<3&&isrev) op=op%2+1; 34 if(op==1&&l[y]==x) continue; 35 else if(op==2&&r[y]==x) continue; 36 int a = l[x]; int b = l[y]; int ry = r[y]; int rx = r[x]; 37 if(op==1){ 38 // remove(x); 39 link(a,rx); 40 // insert_r(x,b); 41 link(b,x); link(x,y); 42 } 43 else if(op==2){ 44 link(a,rx); 45 // remove(x); 46 link(y,x); link(x,ry); 47 // insert_r(x,y); 48 }else if(op==3){ 49 if(r[x]==y){ 50 b = y;//錯因:沒有更新a與b 51 remove(x);remove(y); 52 // link(a,y); link(y,x); link(x,ry); 53 insert_r(y,a); 54 insert_r(x,b); 55 }else if(r[y]==x){ 56 a = x; 57 remove(x);remove(y); 58 // link(b,x); link(x,y); link(y,rx); 59 insert_r(x,b); 60 insert_r(y,a); 61 }else{ 62 remove(x);remove(y); 63 insert_r(x,b); 64 insert_r(y,a); 65 // link(a,y); link(y,rx); 66 // link(b,x); link(x,ry); 67 } 68 } 69 } 70 for(int i=r[0],t = 1;i!=n+1;i=r[i],t++){ 71 if(t%2) sum+=(ll)e[i]; 72 }//這個條件太容易漏了 73 if(isrev&&n%2==0) sum = (ll)(n+1)*n/2-sum; 74 printf("%lld\n",sum); 75 } 76 return 0; 77 }
陣列模擬連結串列

還可以直接用STL的連結串列,這裡利用prev()與vector儲存指向每個元素的迭代器,否則反覆查詢TLE

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef list<int>::iterator lit;
 4 int kcase;
 5 int main()
 6 {
 7     int m,n;
 8     while(scanf("%d%d",&n,&m)==2){
 9         list<int> lst;//擴容 
10         printf("Case %d: ",++kcase);
11         vector<lit> v= {(lit)NULL}; int op,inv = 0,x,y;//v先賦值一個空指標使下標對應 
12         for(int i=1;i<=n;i++){
13             lst.push_back(i);
14             v.push_back(prev(lst.end()));//lst.end()不斷向後移 
15         }
16         while(m--){
17             scanf("%d",&op);
18             if(op==4) { inv = !inv;continue;  } 
19             scanf("%d%d",&x,&y);
20             if(op==3) { iter_swap(v[x],v[y]); swap(v[x],v[y]); } //交換迭代器所指值 交換迭代器
21             if(op<3&&inv) op = op%2+1;
22             if(op==1||op==2){ 
23                 lst.erase(v[x]);
24                 auto p = v[y];
25                 if(op==2) p++;
26                 v[x] = lst.insert(p,x); 
27             }    
28         }
29         long long sum = 0;
30         int k = 1;
31         for(auto it=lst.begin();it!=lst.end();it++,k++){
32             if(k%2) sum+=(long long)(*it);
33         }
34         if(inv&&n%2==0) sum = (long long)(n+1)*n/2-sum;
35         printf("%lld\n",sum);
36     }
37 } 
STL

知識補充

  1. 當給容器裝入指標時,我們如果賦值NULL,需要將它轉化為相應的迭代器型別
  2. insert返回的是插入元素的迭代器,不是下一個
  3. 不能直接*指標的賦值