欢迎访问我的Uva题解目录哦 https://blog.csdn.net/richenyunqi/article/details/81149109

题目描述

例题6-5 移动盒子(Boxes in a Line, UVa 12657)——使用list容器实现,比紫书中算法简单

题意解析

你有一行盒子,从左到右依次编号为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和指令条数m1n,m100000(1≤n,m≤100000),以下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;
}

相关文章: