1. 程式人生 > >算法入門經典第六章 例題6-5 移動盒子

算法入門經典第六章 例題6-5 移動盒子

stl 是否 %d 編號 ret 特殊 第六章 沒有 big

例題 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\n",++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\n",++cnt,ans);
    }
    return 0;
}

算法入門經典第六章 例題6-5 移動盒子