算法入門經典第六章 例題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\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 移動盒子