例题 6-5 移动盒子(Boxes in a Line, UVa127675)
问题 给定一行盒子,从左到右编号依次为1,2,...,n.可以执行以下命令:
1 X Y
把盒子 X 移动到 Y 的左边(如果已经在左边,忽略此命令)
2 X Y
把盒子 X 移动到 Y 右边(如果X已经在Y的右边,忽略此命令)
3 X Y
交换 X 和 Y 的位置
4
把整个顺序颠倒
指令保证合法,即X 不等于 Y, 输入包含不超过10组数据,每组第一行为盒子的数目n和指令的数目m(1<=n,m<=100000)。
Output :For each test case, print the sum of numbers at odd-indexed positions. Positions are numbered 1 to nfrom left to right.
样例输入:
6 4
1 1 4
2 3 5
3 1 6
4
6 3
1 1 4
2 3 5
3 1 6
100000 1
4
样例输出:
Case 1: 12
Case 2: 9
Case 3: 2500050000
如果用数组来求解,复杂度太高,每次要移动大量元素,因此很容易想到用双向链表求解。 当然可以用 自己构造双向链表,或者使用STL的list。
受到前一题的启发,其实可用两个 int
数组来构造双向链表,right 数组表示当前元素的下一个元素的下标,left表示前一个元素的下标。
i | 0 | 1 | 2 | 3 | 4 |
left[i] | 4 | 1 | 2 | 3 | 0 |
right[i] | 1 | 3 | 4 | 2 | 0 |
若交换 2和3 1 2 3 4-> 1 3 2 4 (right[i]) 变成
i代表元素位置下标 sum+=1+2=3
i | 0 | 1 | 2 | 3 | 4 |
left[i] | 4 | 0 | 3 | 1 | 2 |
right[i] | 1 | 3 | 4 | 2 | 0 |
分析:
用到了双向链表。
1。用了两个数组left[maxn],right[maxn]代表当前元素的左边一个或者右边一个,当这个值为0的时候代表不存在!
2。对于4号命令,逆转整个序列,并没有真正的逆转,而是用inv 记录 是否逆转,利用了逆转两次等于没有逆转这个道理。逆转只会影响到1命令和2命令,3命令是XY换一下,并不会影响到,所以对与1和2,直接op = 3 - op即可!利用inv这个变量也有利于最后的输出,最后输出发现inv是0的话,就是没逆转,那么直接把奇数位置的数加起来即可,反之,要用总和减去这个偶数序列,(因为当n是偶数并且逆转的情况,sum其实是偶数位置!)
3。最后注意的一点,对于3号命令,是XY置换,XY相邻和XY相隔很多元素,处理是不一样的。
如果用数组来求解,复杂度太高,每次要移动大量元素,因此很容易想到用双向链表求解。 当然可以用 自己构造双向链表,或者使用STL的list。受到前一题的启发,其实可用两个 int
数组来构造双向链表,rightt
数组表示当前元素的下一个元素的下标,leftt
表示前一个元素的下标。
#include#include using namespace std; const int maxn = 100000 + 10; int rightt[maxn],leftt[maxn];left and righttt is ambiguous void link(int x,int y) { rightt[x]=y; leftt[y]=x; } int main() { int n,m,cnt=0; while(scanf("%d%d",&n,&m) == 2) { for (int i = 0; i <= n; ++i) { leftt[i]=i-1; rightt[i]=(i+1)%(n+1); } leftt[0]=n; rightt[n]=0; int op,inv=0,X,Y; while(m--) { scanf("%d",&op); if (op == 4)inv = !inv; //偶数列 项转置会变成奇数项 1 2 3 4 -> 4 3 2 1 else { scanf("%d%d",&X,&Y); if(op==3&&rightt[Y]==X) //为了转化为同一种情况来处理,x、y只是一个代号,反正结果变得是值 swap(X,Y); if (inv && op != 3)op = 3 - op; if (op == 1 && rightt[X] == Y)continue; if (op == 2 && rightt[Y] == X)continue; int LX=leftt[X],RX=rightt[X],RY=rightt[Y],LY=leftt[Y]; if (op == 1) { link(LX,RX); link(LY,X); link(X,Y); } if (op == 2) { link(LX,RX); link(Y,X); link(X,RY); } //执行命令 3时候,注意如果X和Y是相邻的,需要特殊处理; if (op == 3) { if(rightt[X]==Y) { link(LX,Y); link(Y,X); link(X,RY); } else { link(LX,Y); link(Y,RX); link(LY,X); link(X,RY); } } } } //0 1 2 3 4 ->1 3 2 4 0 交换2和3 结果 1+2 long long ans=0; int b = 0; for (int i = 1; i <= n; ++i) { b = rightt[b]; //向右推移 if (i % 2 == 1)ans+=b; } // //如果n不是偶数的话,倒一下结果也一样 //最后输出发现inv是0的话,就是没逆转,那么直接把奇数位置的数加起来即可,反之,要用总和减去这个偶数序列,(因为当n是偶数并且逆转的情况, //sum其实是偶数位置 if (n % 2 == 0 && inv)ans = (long long)(n+1)*n/2-ans; printf("Case %d: %lld\n",++cnt,ans); } return 0; }
用left right定义数组会有歧义
left
and right
are already defined in namespace std
, which you are importing all of with using namespace std
. That's why you have an ambiguity.
法二:在op==3 分下来三种情况 XY相邻两种 不相邻 1种
#include#include using namespace std; const int maxn = 100000 + 10; int rightt[maxn],leftt[maxn]; void link(int x,int y) { rightt[x]=y; leftt[y]=x; } int main() { int n,m,cnt=0; while(scanf("%d%d",&n,&m) == 2) { for (int i = 0; i <= n; ++i) { leftt[i]=i-1; rightt[i]=(i+1)%(n+1); } leftt[0]=n; rightt[n]=0; int op,inv=0,X,Y; while(m--) { scanf("%d",&op); if (op == 4)inv = !inv; else { scanf("%d%d",&X,&Y); if (inv && op != 3)op = 3 - op; if (op == 1 && rightt[X] == Y)continue; if (op == 2 && rightt[Y] == X)continue; int LX=leftt[X],RX=rightt[X],RY=rightt[Y],LY=leftt[Y]; if (op == 1) { link(LX,RX); link(LY,X); link(X,Y); } if (op == 2) { link(LX,RX); link(Y,X); link(X,RY); } //执行命令 3时候,注意如果X和Y是相邻的,需要特殊处理; if (op == 3) { if(rightt[X]==Y) { link(LX,Y); link(Y,X); link(X,RY); } else if(rightt[Y]==X) { link(LY,X); link(X,Y); link(Y,RX); } else{ link(LX,Y); link(Y,RX); link(LY,X); link(X,RY); } } } } //0 1 2 3 4 ->1 3 2 4 0 交换2和3 结果 1+2 long long ans=0; int b = 0; for (int i = 1; i <= n; ++i) { b = rightt[b]; //向右推移 if (i % 2 == 1)ans+=b; } //如果n不是偶数的话,倒一下结果也一样 //最后输出发现inv是0的话,就是没逆转,那么直接把奇数位置的数加起来即可,反之,要用总和减去这个偶数序列,(因为当n是偶数并且逆转的情况, //sum其实是偶数位置 //偶数列 项转置会变成奇数项 1 2 3 4 -> 4 3 2 1 if (n % 2 == 0 && inv)ans = (long long)(n+1)*n/2-ans; printf("Case %d: %lld\n",++cnt,ans); } return 0; }