• 算法入门经典第六章 例题6-5 移动盒子


    例题 6-5 移动盒子(Boxes in a Line, UVa127675)

    问题 给定一行盒子,从左到右编号依次为1,2,...,n.可以执行以下命令:

    1 X Y 把盒子 X 移动到 Y 的左边(如果已经在左边,忽略此命令)

    2 X Y 把盒子 X 移动到 Y 右边(如果X已经在Y的右边,忽略此命令)

    3 X Y交换 X 和 Y 的位置

    4 把整个顺序颠倒

    指令保证合法,即X 不等于 Y, 输入包含不超过10组数据,每组第一行为盒子的数目n和指令的数目m(1<=n,m<=100000)

    Output  :For each test case, print the sum of numbers at odd-indexed positions. Positions are numbered 1 to nfrom left to right.

    样例输入:

    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
    

    样例输出:

    Case 1: 12
    Case 2: 9
    Case 3: 2500050000

    如果用数组来求解,复杂度太高,每次要移动大量元素,因此很容易想到用双向链表求解。 当然可以用 自己构造双向链表,或者使用STL的list。
    受到前一题的启发,其实可用两个 int 数组来构造双向链表,right 数组表示当前元素的下一个元素的下标,left表示前一个元素的下标。

    i 0 1 2 3 4
    left[i] 4 1 2 3 0
    right[i] 1 3 4 2 0
    若交换 2和3   1 2 3 4-> 1 3 2 4 (right[i])  变成 

    i代表元素位置下标 sum+=1+2=3


    i 0 1 2 3 4
    left[i] 4 0 3 1 2
    right[i] 1 3 4 2 0
     

    分析:

    用到了双向链表。

    1。用了两个数组left[maxn],right[maxn]代表当前元素的左边一个或者右边一个,当这个值为0的时候代表不存在!

    2。对于4号命令,逆转整个序列,并没有真正的逆转,而是用inv 记录 是否逆转,利用了逆转两次等于没有逆转这个道理。逆转只会影响到1命令和2命令,3命令是XY换一下,并不会影响到,所以对与1和2,直接op = 3 - op即可!利用inv这个变量也有利于最后的输出,最后输出发现inv是0的话,就是没逆转,那么直接把奇数位置的数加起来即可,反之,要用总和减去这个偶数序列,(因为当n是偶数并且逆转的情况,sum其实是偶数位置!)

    3。最后注意的一点,对于3号命令,是XY置换,XY相邻和XY相隔很多元素,处理是不一样的。

    如果用数组来求解,复杂度太高,每次要移动大量元素,因此很容易想到用双向链表求解。 当然可以用 自己构造双向链表,或者使用STL的list。受到前一题的启发,其实可用两个 int 数组来构造双向链表,rightt数组表示当前元素的下一个元素的下标,leftt表示前一个元素的下标。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 100000 + 10;
    int rightt[maxn],leftt[maxn];////left and righttt is ambiguous 
    void link(int x,int y)
    {
        rightt[x]=y;
        leftt[y]=x;
    }
    int main()
    {
        int n,m,cnt=0;
        while(scanf("%d%d",&n,&m) == 2)
        {
            for (int i = 0; i <= n; ++i)
            {
                leftt[i]=i-1;
                rightt[i]=(i+1)%(n+1);
            }
            leftt[0]=n;
            rightt[n]=0;
            int op,inv=0,X,Y;
            while(m--)
            {
                scanf("%d",&op);
                if (op == 4)inv = !inv;  //偶数列 项转置会变成奇数项 1 2 3 4 -> 4 3 2 1
                else
                {
                    scanf("%d%d",&X,&Y);
                    if(op==3&&rightt[Y]==X) //为了转化为同一种情况来处理,x、y只是一个代号,反正结果变得是值
                        swap(X,Y);
    
                    if (inv && op != 3)op = 3 - op;
                    if (op == 1 && rightt[X] == Y)continue;
                    if (op == 2 && rightt[Y] == X)continue;
                    int LX=leftt[X],RX=rightt[X],RY=rightt[Y],LY=leftt[Y];
                    if (op == 1)
                    {
                        link(LX,RX);
                        link(LY,X);
                        link(X,Y);
                    }
                    if (op == 2)
                    {
                        link(LX,RX);
                        link(Y,X);
                        link(X,RY);
                    }
    //执行命令 3时候,注意如果X和Y是相邻的,需要特殊处理;
                    if (op == 3)
                    {
                        if(rightt[X]==Y)
                        {
                            link(LX,Y);
                            link(Y,X);
                            link(X,RY);
                        }
                        else
                        {
                            link(LX,Y);
                            link(Y,RX);
                            link(LY,X);
                            link(X,RY);
                        }
    
    
                    }
                }
            }
            //0 1 2 3 4 ->1 3 2 4 0 交换2和3 结果 1+2
            long long ans=0;
            int b = 0;
            for (int i = 1; i <= n; ++i)
            {
                b = rightt[b];  //向右推移
                if (i % 2 == 1)ans+=b;
            }
    //
    //如果n不是偶数的话,倒一下结果也一样
    //最后输出发现inv是0的话,就是没逆转,那么直接把奇数位置的数加起来即可,反之,要用总和减去这个偶数序列,(因为当n是偶数并且逆转的情况,
    
    //sum其实是偶数位置
    
            if (n % 2 == 0 && inv)ans = (long long)(n+1)*n/2-ans;
            printf("Case %d: %lld
    ",++cnt,ans);
        }
        return 0;
    }

    用left right定义数组会有歧义

    left and right are already defined in namespace std, which you are importing all of with using namespace std. That's why you have an ambiguity. 

    法二:在op==3 分下来三种情况 XY相邻两种 不相邻 1种

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 100000 + 10;
    int rightt[maxn],leftt[maxn];
    void link(int x,int y)
    {
        rightt[x]=y;
        leftt[y]=x;
    }
    int main()
    {
        int n,m,cnt=0;
        while(scanf("%d%d",&n,&m) == 2)
        {
            for (int i = 0; i <= n; ++i)
            {
                leftt[i]=i-1;
                rightt[i]=(i+1)%(n+1);
            }
            leftt[0]=n;
            rightt[n]=0;
            int op,inv=0,X,Y;
            while(m--)
            {
                scanf("%d",&op);
                if (op == 4)inv = !inv;
                else
                {
                    scanf("%d%d",&X,&Y);
                    if (inv && op != 3)op = 3 - op;
                    if (op == 1 && rightt[X] == Y)continue;
                    if (op == 2 && rightt[Y] == X)continue;
                    int LX=leftt[X],RX=rightt[X],RY=rightt[Y],LY=leftt[Y];
                    if (op == 1)
                    {
                        link(LX,RX);
                        link(LY,X);
                        link(X,Y);
                    }
                    if (op == 2)
                    {
                        link(LX,RX);
                        link(Y,X);
                        link(X,RY);
                    }
    //执行命令 3时候,注意如果X和Y是相邻的,需要特殊处理;
                    if (op == 3)
                    {
                        if(rightt[X]==Y)
                        {
                            link(LX,Y);
                            link(Y,X);
                            link(X,RY);
                        }
                        else if(rightt[Y]==X)
                        {
                            link(LY,X);
                            link(X,Y);
                            link(Y,RX);
    
                        }
                        else{
                            link(LX,Y);
                            link(Y,RX);
                            link(LY,X);
                            link(X,RY);
                        }
    
    
                    }
                }
            }
            //0 1 2 3 4 ->1 3 2 4 0 交换2和3 结果 1+2
            long long ans=0;
            int b = 0;
            for (int i = 1; i <= n; ++i)
            {
                b = rightt[b];  //向右推移
                if (i % 2 == 1)ans+=b;
            }
    //如果n不是偶数的话,倒一下结果也一样
    //最后输出发现inv是0的话,就是没逆转,那么直接把奇数位置的数加起来即可,反之,要用总和减去这个偶数序列,(因为当n是偶数并且逆转的情况,
    
    //sum其实是偶数位置
    //偶数列 项转置会变成奇数项 1 2 3 4 -> 4 3 2 1
            if (n % 2 == 0 && inv)ans = (long long)(n+1)*n/2-ans;
            printf("Case %d: %lld
    ",++cnt,ans);
        }
        return 0;
    }
  • 相关阅读:
    红黑树(二)插入
    HDU 3415 Max Sum of Max-K-sub-sequence(单调队列)
    Codeforces 433 Div.2(A、B、C、D)
    Codeforces 846D Monitor(简单二分+二维BIT)
    hihoCoder 1403 后缀数组一·重复旋律(后缀数组+单调队列)
    CF 787D Legacy(线段树思想构图+最短路)
    HDU 6165 FFF at Valentine(Tarjan缩点+拓扑排序)
    Codeforces 165E Compatible Numbers(二进制+逆序枚举)
    Codeforces 672D Robin Hood(二分好题)
    HITOJ 2739 The Chinese Postman Problem(欧拉回路+最小费用流)
  • 原文地址:https://www.cnblogs.com/is-Tina/p/7398229.html
Copyright © 2020-2023  润新知