6 5 Boxes in a Line UVa12657 - nuaazdh/acm GitHub Wiki
##例题 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)。
样例输入:
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
数组来构造双向链表,next
数组表示当前元素的下一个元素的下标,prev
表示前一个元素的下标。
###code in C++
#include <iostream>
const int max_size = 100100;
int prev[max_size];
int next[max_size];
void swap_int(int &x, int &y) {
int tmp = x;
x = y;
y = tmp;
}
void print_order() {
int i;
for (i = next[0]; i != 0; i = next[i]) {
std::cout << i << " ";
}
std::cout << "\n";
}
void print_vorder()
{
int i;
for (i = prev[0]; i != 0; i = prev[i]) {
std::cout << i << " ";
}
std::cout << "\n";
}
int main() {
int m, n, cmd, x, y, i, j, inv = 0, kcase = 0;
unsigned long long sum;
while (std::cin >> m >> n && m && n) {
sum = 0;
inv = 0;
for (i = 1; i <= m; ++i) {
next[i] = i + 1;
prev[i] = i - 1;
}
next[0] = 1;
next[m] = 0;
prev[0] = m;
for (i = 0; i < n; ++i) {
std::cin >> cmd;
if (cmd == 4) {
inv = 1 - inv;
} else {
if (inv) {
if (cmd == 1) cmd = 2;
else if (cmd == 2) cmd = 1;
if (cmd == 3) swap_int(x, y);
}
std::cin >> x >> y;
if (cmd == 1) {
if (next[x] == y) {
continue;
}
prev[next[x]] = prev[x];
next[prev[x]] = next[x];
next[prev[y]] = x;
prev[x] = prev[y];
prev[y] = x;
next[x] = y;
} else if (cmd == 2) {
if (prev[x] == y) {
continue;
}
if (next[x]) prev[next[x]] = prev[x];
next[prev[x]] = next[x];
if(next[y]) prev[next[y]] = x;
next[x] = next[y];
prev[x] = y;
next[y] = x;
} else if (cmd == 3) {
if(prev[x] == y)
swap_int(x,y);
if(prev[y] == x){
next[prev[x]] = y;
prev[next[y]] = x;
prev[y] = prev[x];
next[x] = next[y];
prev[x] = y;
next[y] = x;
}else{
prev[next[x]] = y;
next[prev[x]] = y;
prev[next[y]] = x;
next[prev[y]] = x;
swap_int(prev[x], prev[y]);
swap_int(next[x], next[y]);
}
}
}// else
// print_order();
// print_vorder();
}//for
j = 1;
for (i = next[0]; i != 0; i = next[i], j++) {
if (inv && !(m & 1)) {
if (j & 1) continue;
} else {
if (!(j & 1)) continue;
}
sum += i;
}
std::cout << "Case " << ++kcase << ": " << sum << "\n";
}
return 0;
}
###点评 调试了很久才AC,主要的注意点:
-
next
和prev
的初始化,
next[0]=0;
prev[0]=m;
- 执行命令 3时候,注意如果X和Y是相邻的,需要特殊处理;
- 对命令4的特殊处理,如果每次都调整代价太大,很容易超时,这里使用了一个标记;
###改进 书中采用了一个新的方法,思路大致相同,但是不在调整指针(prev和next )的值,而是通过一个 link函数来连接链表的节点。
void link(int L, int R)
{
next[L] = R;
prev[R] = L;
}