UVA ~ 12657 ~ Boxes in a Line (双向链表)

题意:你有一行盒子,从左到右依次编号为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时在初始状态下执行1 1 4后,盒子序列为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(1<=n,m<=100000),以下m行每行包含一条指令。每组数据输出一行,即所有奇数位置的盒子编号之和。位置从左到右编号为1~n。

【分析】

根据前面的经验,如果用数组来保存盒子,肯定会超时,但如果像例题6-4那样只保存个next值。似乎又不够,怎么办?解决方法是采用双向链表(doubly linked list) :用left[i]和right[i]分别表示编号为i的盒子左边和右边的盒子编号(如果是0,表示不存在),则下面的过程可以让两个结点相互连接 void link(int L,int R){

 right[L] = R: left[R] = L;

}

提示6-5: 在双向链表这样    的复杂链式结构中,往往会编写一些辅助函数用来设置链接关系。
有了这个代码,可以先记录好操作之前X和Y两边的结点,然后用link函数按照某种顺序把它们连起来。操作4 比较特殊,为了避免一次修改所有元素的指针,此处增加一个记inv,表示有没有执行过操作4(如果inv=l 时再执行一次操作4,则inv变为0)。这样,当p为1和2且inv=1时,只需把OP变成3-op(注意操作3不受inv影响) 即可。最终验出时要根据inv的值进行不同处理。
提示6-6: 如果数据结构上的某一个操作很耗时,有时可以用加标记的方式处理,而不需要真的执行那个操作。但同时,该数据结构的所有其他操作都要考虑这个标记。
下面的核心代码里还有一些可以借鉴的细节处理,请读者仔细阅读:


#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m, Left[MAXN], Right[MAXN];
void link(int L, int R) { Right[L] = R; Left[R] = L; }
int main()
{
    int CASE = 1;
    while (~scanf("%d%d", &n, &m))
    {
        for (int i = 0; i <= n+1; i++) Left[i] = i-1, Right[i] = i+1;
        int op, X, Y;
        bool inv = false;//是否反转
        while (m--)
        {
            scanf("%d", &op);
            if (op == 4) inv = !inv;
            else
            {
                scanf("%d%d", &X, &Y);
                if (inv && op != 3) op = 3 - op;//如果链表反转了,交换1,2操作
                if (op == 3 && Right[Y] == X) swap(X, Y);//对于3操作中XY和YX这种情况->XY
                int LX = Left[X], RX = Right[X], LY = Left[Y], RY = Right[Y];
                if (op == 1 && X != Left[Y]) { link(LX, RX); link(LY, X); link(X, Y); }
                if (op == 2 && X != Right[Y]) { link(LX, RX); link(Y, X); link(X, RY); }
                if (op == 3)
                {
                    if (Right[X] == Y) { link(LX, Y); link(Y, X); link(X, RY); }
                    else { link(LX, Y); link(Y, RX); link(LY, X); link(X, RY); }
                }
            }
        }
        int b = 0;
        long long ans = 0;
        for (int i = 1; i <= n; i++)
        {
            b = Right[b];
            if (i%2) ans += b;
        }
        if (inv && n%2 == 0) ans = (long long)n*(n+1)/2 - ans;
        printf("Case %d: %lld\n", CASE++, ans);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/zscdst/article/details/80271641