UVA-12657 Boxes in a Line (模擬,雙向連結串列(陣列實現))
阿新 • • 發佈:2018-11-11
第一次對陣列實現雙向連結串列的練習,理解了發現數組實現真方便…
題目:UVA-12657
題目描述:
你有n個盒子在桌子上的一條線上從左到右編號為1……n。你的任務是模擬四種操作
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
分析:
這題用陣列實現雙向連結串列極其便利,因為這裡的編號從1~n,即是 值 ,也是 下標(可以理解為node的地址),所以不需要另外建立node陣列來聯絡這兩者,直接就能處理。
分析一下題目,1,2操作直接模擬即可,3操作最好要對X,Y相鄰的情況進行特判(不知道其他方法是否可省略特判,反正我WA了 )。
操作4最為關鍵,如果每次都反轉會花很多時間,一開始還不知道怎麼處理,但其實反轉只會影響操作1和操作2以及最後求和,如果反轉了,操作1就變成了操作2,操作2變成了操作1,求和找奇數位置時從頭到尾遍歷變成從尾到頭。
(還有,如果用switch語句來選擇運算元,記得每個case結尾break;,不要問我為什麼說這個
貼下程式碼(裡面還有註釋):
#include<cstdio>
using namespace std;
int Right[100001], Left[100001];
void link(int L, int R) //構造一個函式來連線左右兩個,可以讓程式簡潔明瞭很多
{
Right[L] = R;
Left[R] = L;
}
int main()
{
int n, m,kase=1;
while (scanf("%d%d",&n,&m)!=EOF)
{
int i, k, x, y,head,tail;
bool flag = true; //用來標記操作4,true不反轉,false反轉
for (i = 1; i <=n; i++) //i即是編號,又是下標,直接串成雙向連結串列
link(i - 1, i);
head = 0; //head做頭節點
Left[head] = 0;
tail = n + 1; //這個tail還是很重要的,不然連結串列第n個的操作就不方便了
link(n, tail);
Right[tail] = 0;
while (m--)
{
scanf("%d", &k);
if (flag==false) //當發現反轉,僅影響操作1、操作2
{
if (k == 1)
k = 2;
else if (k == 2)
k = 1;
}
switch (k)
{
case 1:
{
scanf("%d%d", &x, &y);
if (Left[y] == x)
break;
else
{
link(Left[x], Right[x]);
link(Left[y], x);
link(x, y);
}
break;
}
case 2:
{
scanf("%d%d", &x, &y);
if (Right[y] == x)
break;
else
{
link(Left[x], Right[x]);
link(x, Right[y]);
link(y, x);
}
break;
}
case 3: //注意特判相鄰情況
{
scanf("%d%d", &x, &y);
if (Right[x] == y)
{
link(x, Right[y]);
link(Left[x], y);
link(y, x);
}
else if (Left[x] == y)
{
link(y, Right[x]);
link(Left[y], x);
link(x, y);
}
else
{
int xleft = Left[x], xright = Right[x], yleft = Left[y], yright = Right[y];
link(yleft, x); //把x,y的左右都暫存,這樣方便些,不然移動過程會發生變動
link(x, yright);
link(xleft, y);
link(y, xright);
}
break;
}
case 4:flag = !flag; //反轉
}
}
long long sum = 0;
if (flag==true) //正序查詢奇數位置,從head到tail
{
i = Right[head];
while (1)
{
sum += i;
if (Right[i] == tail)
break;
else
i = Right[i];
if (Right[i] == tail)
break;
else
i = Right[i];
}
}
else //反轉後倒序查詢奇數位置,從tail到head
{
i = Left[tail];
while (1)
{
sum += i;
if (Left[i] == head)
break;
else
i = Left[i];
if (Left[i] == head)
break;
else
i = Left[i];
}
}
printf("Case %d: %lld\n",kase++,sum);
}
return 0;
}