欢迎访问我的Uva题解目录哦 https://blog.csdn.net/richenyunqi/article/details/81149109
题目描述
题意解析
你有一行盒子,从左到右依次编号为1, 2, 3,…, n。可以执行以下4种指令:
- 1 X Y表示把盒子X移动到盒子Y左边(如果X已经在Y的左边则忽略此指令)。
- 2 X Y表示把盒子X移动到盒子Y右边(如果X已经在Y的右边则忽略此指令)。
- 3 X Y表示交换盒子X和Y的位置。
- 4表示反转整条链。
指令保证合法,即X不等于Y。例如,当n=6时在初始状态下执行114后,盒子序列为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。
输入包含不超过10组数据,每组数据第一行为盒子个数n和指令条数m,以下m行每行包含一条指令。每组数据输出一行,即所有奇数位置的盒子编号之和。位置从左到右编号为1~n。
算法设计
由于涉及到在容器中间插入和删除元素,可以用list容器来存储数据。要注意在list容器中插入和删除元素其他迭代器均不会失效。C++容器类插入和删除时迭代器的失效情况可参考我的博客C++容器类插入和删除时迭代器的失效情况总结。
为了快速查找到元素在list中的位置,可以另外使用一个vector容器it来存储每个元素在list中的迭代器。
对于指令4,由于翻转整个链表中所有元素非常耗时,而两次翻转元素即为保持原状。可以定义一个变量r标志是否翻转一次链表。等所有操作结束之后再根据r的值考虑是否翻转链表。
对于指令3,是否翻转链表对该指令没有影响。那么在vector容器it找到指向元素x和元素y的迭代器,交换链表中这两个元素,再交换it中这两个迭代器即可。
对于指令1和2,是否翻转链表对该指令有影响,如果需要翻转链表(实际上没有翻转),则指令1向y左边插入x,则相当于指令2向y右边插入x;指令2同样也变为指令1。那么只需删除链表中的元素x,然后在链表中y的相应位置再插入元素x即可。
C++代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m,op,x,y;
for(int ii=1;~scanf("%d%d",&n,&m);++ii){
list<int>line(n);//链表
vector<list<int>::iterator>it(n+1);//存储迭代器的数组
bool r=false;//标志是否需要翻转链表
auto i=line.begin();
for(int j=1;j<=n;++j){//初始化链表line和存储迭代器的数组it
*i=j;
it[j]=i++;
}
while(m--){
scanf("%d",&op);
if(op==4){//指令4,只需更改r,不需要真的翻转链表
r=!r;
continue;
}
scanf("%d%d",&x,&y);
if(op==3){//指令3,交换x和y元素
swap(*it[x],*it[y]);
swap(it[x],it[y]);
continue;
}
auto yLeft=it[y],yRight=it[y];//找到指向y左边元素和y右边元素的迭代器
--yLeft;
++yRight;
if(r)//需要翻转链表
op=3-op;//指令1变为指令2,指令2变为指令1
if(op==1&&it[x]!=yLeft){//指令1且x没有在y的左边
line.erase(it[x]);//在链表中删除x
it[x]=line.insert(it[y],x);//在y左边插入元素x
}else if(op==2&&it[x]!=yRight){//指令2且x没在y的右边
line.erase(it[x]);//在链表中删除x
it[x]=line.insert(yRight,x);//在y右边插入元素x
}
}
long long ans=0;//存储结果
if(r)//需要翻转链表
line.reverse();
i=line.begin();
for(int j=1;j<=n;++j,++i)
if(j%2==1)
ans+=*i;
printf("Case %d: %lld\n",ii,ans);
}
return 0;
}