UVa12657
将顺序编号的盒子排成一列,模拟处理下面四种指令:
- 1 X Y,将
X
挪到Y
的左边 - 2 X Y,将
X
挪到Y
的右边 - 3 X Y,交换
X
和Y
- 4,反转整个序列
由于涉及在线性序列中间位置的插入和删除,因此还是应该使用链表,但是使用C++标准库中的list
还是会超时,其原因在于模拟的过程中存在多次的查找和删除,因此应该空间换时间来避免查找和删除。
为了避免在查找时进行遍历,可以记录每个盒子的相对位置,也就是使用数组模拟双向链表,这样在进行查找和删除时就可以直接定位到对应的箱子,后面只需要更新盒子之间的链接关系即可。
稍微复杂一些的操作是交换,因为双向链表的交换操作需要处理元素相邻的特殊情况。代码中使用的方式是先删除X
,然后将X
插入到Y
的右面,此时如果初始时X
不在Y
的左面,则在将Y
删除并移动到X
的位置——这种方式只适用于X
和Y
不相邻或者X
在Y
的左面的情况。
麻烦的还有输出,由于链表长度可奇可偶,且只输出奇数位置盒子编号的和,为了简化判断是否到达链表尾部的逻辑,代码中使用count
变量提前计算出了需要求和元素的数量。
#include <iostream>
#include <vector>
using namespace std;
struct Box
{
int no;
int left, right;
Box(int n, int l, int r) : no(n), left(l), right(r) {};
};
struct Boxes
{
vector<Box> boxes;
Boxes(int n)
{
boxes.emplace_back(0, 0, 1);
for (int i = 1; i <= n; i++)
{
boxes.emplace_back(i, i - 1, i + 1);
}
boxes.emplace_back(0, n, 0);
}
void remove(int X)
{
Box &x = boxes[X];
boxes[x.left].right = x.right;
boxes[x.right].left = x.left;
}
void InsertLeft(int X, int Y)
{
Box &y = boxes[Y];
boxes[X].right = Y;
boxes[X].left = y.left;
boxes[y.left].right = X;
y.left = X;
}
void InsertRight(int X, int Y)
{
Box &y = boxes[Y];
boxes[X].left = Y;
boxes[X].right = y.right;
boxes[y.right].left = X;
y.right = X;
}
void swap(int X, int Y)
{
if (boxes[Y].right == X) return swap(Y, X);
int XRight = boxes[X].right;
remove(X);
InsertRight(X, Y);
if (XRight != Y) {
remove(Y);
InsertLeft(Y, XRight);
}
}
unsigned int GetOddSum(bool reverse)
{
unsigned int sum = 0;
size_t count = (boxes.size() - 2) / 2 + boxes.size() % 2;
if (reverse) {
int no = boxes.back().left;
for (size_t i = 0; i < count; i++)
{
sum += no;
no = boxes[no].left;
no = boxes[no].left;
}
}
else {
int no = boxes.front().right;
for (size_t i = 0; i < count; i++)
{
sum += no;
no = boxes[no].right;
no = boxes[no].right;
}
}
return sum;
}
};
int main()
{
int cases = 1;
int n, m;
while (cin >> n) {
Boxes boxes(n);
cin >> m;
bool reverse = false;
int cmd, X, Y;
for (int i = 0; i < m; i++)
{
cin >> cmd;
switch (cmd)
{
case 1:
cin >> X >> Y;
boxes.remove(X);
if (reverse) boxes.InsertRight(X, Y);
else boxes.InsertLeft(X, Y);
break;
case 2:
cin >> X >> Y;
boxes.remove(X);
if (reverse) boxes.InsertLeft(X, Y);
else boxes.InsertRight(X, Y);
break;
case 3:
cin >> X >> Y;
boxes.swap(X, Y);
break;
case 4:
reverse = reverse == false;
break;
}
}
cout << "Case " << cases++ << ": " << boxes.GetOddSum(reverse) << endl;
}
return 0;
}
/*
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
*/