题解 UVA12657 【Boxes in a Line】
没什么好说的,就是用链表模拟就行了
为什么要用链表模拟呢?
因为纯数组的话每一次执行命令1,命令2会需要移动大量元素,导致TLE。
链表的话有两种实现方法,指针和数组。本人更喜欢用指针写链表,因为这样可以不去考虑数组开的不够大等等情况。
思路如下:
对于1和2,移动就相当于先删除x,再在指定位置(y前或后)插入一个x。
对于3,直接交换x和y的地址和值
对于4,直接加标记(不懂的看紫书)
代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
struct Lst{ //链表
Lst *prev, *next; //prev是前驱(即上一个元素),next是后继(即下一个元素)
int value;//元素的值
Lst(int val = 0): prev(0), next(0), value(val){}
//构造函数,构造得到一个值为val的节点,默认为0
};
Lst *End; //建一个虚拟节点
Lst* addr[100000 + 5]; //保存每一个值(每一个盒子)所对应的地址
int size; //链表的大小
void csh(){ //初始化,即构造虚拟节点
End = new Lst; //分配内存
End->prev = End; //前驱指向自己,下面后继一样的
End->next = End;
}
Lst* Insert(Lst *it, int val){ //插入元素,把值为val的元素插到地址it的后面
Lst *it2 = new Lst(val);
it2->next = it->next;
it->next->prev = it2;
it2->prev = it;
it->next = it2;
size++; //大小+1
return addr[val]=it2; //保存val的地址
}
Lst* Erase(Lst *it){ //删除地址为it的元素
Lst *res = it->next;
it->next->prev = it->prev;
it->prev->next = it->next;
addr[it->value] = 0;
delete it; //立即释放内存,避免内存泄漏
size--; //大小-1
return res;
}
int main(){
csh(); //初始化见上面
int m, n, k = 0;//k用于输出"Case"后面的数字,因此要赋初值(我因此处没有赋初值WA了好多次,更气的是在本地运行时编译器居然给他赋了初值0,结果找了半天才找出来错)
bool rev = false; //标记是否被反转(不懂的自己看紫书)
while(cin>> n>> m){
for(int i = 1; i <= n; ++i)
Insert(End, n - i + 1); //插入节点,不知道为什么插入“i”得到一条反过来的链
while(m--){
int op;
cin>> op;
switch(op){
case 1:{
int a, b;
cin>> a>> b;
Erase(addr[a]);
if(rev) Insert(addr[b], a);//*
else Insert(addr[b]->prev, a);//*
break;
}
case 2:{
int a, b;
cin>> a>> b;
Erase(addr[a]);
if(rev) Insert(addr[b]->prev, a);//*
else Insert(addr[b], a);//*
break;
}
case 3:{
int a, b;
cin>> a>> b;
addr[a]->value = b;
addr[b]->value = a;//交换a,b(题目中x,y)的值
swap(addr[a], addr[b]);//交换a,b的地址
break;
}
case 4:
rev = !rev;
break;
}
/*调试语句 if(rev){
for(Lst *it = End->prev; it != End; it = it->prev)
cout<<(it->value)<<' ';
}
else{
for(Lst *it = End->next; it != End; it = it->next)
cout<<(it->value)<<' ';
}
cout<<endl; */
}
unsigned long long ans = 0;
int s = -1;
cout<<"Case "<<++k<<": ";
if(rev){//*
for(Lst *it = End->prev; it != End; it = it->prev, s = ~s)
if(s & 1){
ans += (it->value);
// cout<<(it->value)<<' ';
}
}
else{//*
for(Lst *it = End->next; it != End; it = it->next, s = ~s)
if(s & 1){
ans += (it->value);
// cout<<(it->value)<<' ';
}
}
// cout<<endl;
cout<<ans<<endl;
rev = false;
while(size) Erase(End->next);
}
return 0;
}
注:打星号的语句分反转和没有反转2种情况,请读者自行体会