1. 程式人生 > >UVA-12657 Boxes in a Line (模擬,雙向連結串列(陣列實現))

UVA-12657 Boxes in a Line (模擬,雙向連結串列(陣列實現))

第一次對陣列實現雙向連結串列的練習,理解了發現數組實現真方便…

題目: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; }

在這裡插入圖片描述