移动盒子——UVa 12657
问题描述
你有一行盒子,从左到右依次编号为1, 2, 3,…, n。可以执行以下4种指令:
- X Y表示把盒子X移动到盒子Y左边(如果X已经在Y的左边则忽略此指令)。
- X Y表示把盒子X移动到盒子Y右边(如果X已经在Y的右边则忽略此指令)。
- X Y表示交换盒子X和Y的位置。
- 表示反转整条链。
指令保证合法,即X不等于Y。例如,当n=6时在初始状态下执行1 1 4
后,盒子序列为2 3 1 4 5 6
。接下来执行2 3 5
,盒子序列变成2 1 4 5 3 6
。再执行3 1 6
,得到2 6 4 5 3 1
。最终执行4
,得到1 3 5 4 6 2
。
想法
此题用双向链表非常好实现,但是蛋疼的是STL有点慢,前一篇悲剧文本——UVA 11988已经说了。如果不熟悉数组模拟链表的建议看看前一篇。
所以还要用数组模拟链表,而且这次是双向链表。
#include "iostream"
#include "cstdio"
#define MAX 100002
using namespace std;
int box_n,cmd_n;
int i;
int cmd,op1,op2,inv;
int _prev[MAX] , _next[MAX];
void link(int l, int r) {
_next[l] = r;_prev[r] = l;
}
int main() {
while (scanf("%d %d", &box_n,&cmd_n) != EOF) {
inv = 0;
_next[0] = 1;
_prev[0] = box_n;
for (i = 1; i <= box_n; i++) {
_next[i] = (i + 1) %(box_n+1);
_prev[i] = i - 1;
}
for (i = 0; i < cmd_n; i++) {
scanf("%d", &cmd);
int nop1, nop2, pop1, pop2;
if (cmd != 4) {
scanf("%d %d", &op1, &op2);
nop1 = _next[op1]; nop2 = _next[op2];
pop1 = _prev[op1]; pop2 = _prev[op2];
}
if (cmd!=3&&inv)cmd = 3 - cmd;
if (cmd == 1 && _prev[op2] == op1)continue;
if (cmd == 2 && _next[op2] == op1)continue;
switch (cmd) {
case 1:
link(pop1, nop1);link(pop2, op1); link(op1, op2);
break;
case 2:
link(pop1, nop1); link(op2, op1);link(op1, nop2);
break;
case 3:
if (_next[op1] == op2) {
link(pop1, op2); link(op2, op1); link(op1, nop2);
}
else if (_next[op2] == op1) {
link(op2, nop1); link(op1, op2); link(pop2, op1);
}
else {
link(pop1, op2); link(op1, nop2); link(op2, nop1); link(pop2, op1);
}
break;
case 4:
inv = !inv;
break;
}
}
if(!inv)
for (i = 0; _next[i] != 0; i = _next[i]) {
printf("%d ", _next[i]);
}
else
for (i = 0; _prev[i] != 0; i = _prev[i]) {
printf("%d ", _prev[i]);
}
printf("\n");
}
return 0;
}
这个代码不难,最主要的是去自己推理那些link
操作的含义,把它比作真的双向链表,其实挺好看出来(我看了俩小时)。