1. 程式人生 > 其它 >[ Uva 101 詳解 ] the block problem 木塊問題

[ Uva 101 詳解 ] the block problem 木塊問題

題目

輸入 n
編號 0 ~ n-1 的木塊,分別擺放在順序排列編號為 0 ~ n-1 的位置。,要求模擬以下 4 種操作(下面的 a 和 b 都是木塊編號)

  • move a onto b:把 a 和 b 上方的木塊全部歸位,然後把 a 摞在 b 上面
  • move a over b:把 a 上方的木塊全部歸位,然後把 a 放在 b 所在木塊堆的頂部
  • pile a onto b:把 b 上方的木塊全部歸位,然後把 a 及上面的木塊整體摞在 b 上面
  • pile a over b:把 a 及上面的木塊整體摞在 b 所在木塊堆的頂部

遇到 quit 時終止一組資料。a 和 b 在同一堆的指令是非法指令,應當忽略。

Input

10
move 9 onto 1
move 8 over 1
move 7 over 1
move 6 over 1
pile 8 over 6
pile 8 over 5
move 2 over 1
move 4 over 9
quit

Output

 0: 0
 1: 1 9 2 4
 2:
 3: 3
 4:
 5: 5 8 7 6
 6:
 7:
 8:
 9:

分析

資料儲存:
n 表示有多少位置,觀察輸出結果後,可以用一個定長陣列來儲存位置,在那個位置上的方塊數量會根據操作增加或減少,屬於不定長,可以使用 vector 陣列來儲存

vector<int> pile[100]; // 用這條來建立一個 有100個位置,每個位置一個不定長陣列的陣列來儲存

操作分析:
觀察 4 個命令,大體上動作只有 歸位 和 移動,可以定義為兩個函式,方便複用

接著 4 個命令放一起對比,4 個命令都有 移動 的動作,這意味著我們只需要判斷何時進行 歸位 的動作即可

前兩個命令進行對比後,似乎帶有 move 的都會讓 a 歸位,有 onto 的會把 b 歸位

後兩個命令也是如此,如果既沒有 move 又沒有 onto,則只進行 移動 動作

操作實現:
首先是歸位,即把 a 以上的木塊放回與其編號對應的位置,那麼我們需要知道當前 a 所在的位置和處在這個位置中的什麼地方,
假定用 p 來表示位置,h 來表示高度,
預設時,編號為 1 的塊就在 p = 1, h = 0 的位置,
移動到編號為 2 塊上面時,位置變為 p = 2, h = 1 (注意每個位置預設是有一個塊的)
那麼如何表示位置便清晰了,
接著是如何找到位置,很明顯這個操作是會經常複用的,我們來定義一個 find 函式來實現這個功能

void find_block(int a, int& p, int& h)                // a 是查詢的木塊編號 ,p 和 h 採用引值傳導,這樣就能修改主函式中位置
{
	for ( p = 0; p < n; p++)                      // 從一切的起點開始遍歷,找到對應則直接退出
	{
		for ( h = 0; h < pile[p].size(); h++) // vector容器的 .size() 方法可以返回不定長陣列此時的長度,避免越界
		{
			if (a == pile[p][h]) return;
		}
	}
}

那麼現在我們已經可以得到任意一個編號木塊的位置了,接著實現把上面的木塊一個個歸位的動作

void goback(int p, int h)
{       int num = 0;
	for (int i = h+1; i < pile[p].size(); i++) // 從當前高度+1處開始歸位
	{      num = pile[p][i];                   // 臨時儲存,你也可以直接用 pile[p][i] 代替 mun,不過那樣的話下一行就太過複雜
		pile[num].push_back(num);          // 使用 vector 的 .push_back() 方法,可以將一個值新增到對應不定長陣列中的最後
	}
	pile[p].resize(h + 1);                    // .resize() 則是將陣列重新限定長度,不夠的補齊,多的直接捨棄,因為編號是從0開始,所以要想儲存 當前號,需要 +1
}

歸位寫好之後,移動就很簡單了

void move(int p1, int h1, int p2)
{
	for (int i = h1; i < pile[p1].size(); i++) // 這邊之所以不對 i +1,因為移動的是 a及a以上
	{
		pile[p2].push_back(pile[p1][i]);  // 同理
	}
	pile[p1].resize(h1);                      // 當前值也刪去
}

目前關鍵操作已經清晰了,接下來就是潤色潤色,新增一些細節

細節處理:
命令輸入是字串混合數字,使用getchar一個一個讀實在麻煩,scanf又不太好實現
所以我們採用流輸入,並且用字串搭配使用,引入 iostream庫 和 string庫

cin >> s1 >> a >> s2 >> b; //可以直接處理掉

成品程式碼

#include <iostream>
#include <vector>
#include <cstdio>
#include <string>


using namespace std;

const int MAXN = 100;
vector<int> pile[MAXN];
int n = 0;



void find_block(int a, int& p, int& h);
void goback(int p, int h);
void move(int p1, int h1, int p2);

int command();



int main()
{
	while (scanf_s("%d", &n) != EOF)
	{
		for (int i = 0; i < n; i++)
		{
			pile[i].clear();
			pile[i].push_back(i);
		}

		while (command());											// 開始處理


		for (int i = 0; i < n; i++)									// 輸出結果
		{
			printf("%d :", i);
			for (int j = 0; j < pile[i].size(); j++)
			{
				printf("  %d", pile[i][j]);
			}
			printf("\n");
		}


	}
}

int command()
{
	int a, b;
	string s1, s2;
	int n1 = 0;
	for (;;)
	{
		int p1, h1, p2, h2;
		cin >> s1 >> a >> s2 >> b;
		if (s1 == "quit") return 0;
		find_block(a, p1, h1);
		find_block(b, p2, h2);
		if (p1 == p2) continue;
		if (s2 == "onto") goback(p2, h2);
		if (s1 == "move") goback(p1, h1);
		move(p1, h1, p2);
	}
}

void find_block(int a, int& p, int& h)
{
	for ( p = 0; p < n; p++)
	{
		for ( h = 0; h < pile[p].size(); h++)
		{
			if (a == pile[p][h]) return;
		}
	}
}

void goback(int p, int h)
{
	for (int i = h+1; i < pile[p].size(); i++)
	{
		pile[i].push_back(i);
	}
	pile[p].resize(h);
}

void move(int p1, int h1, int p2)
{
	for (int i = h1; i < pile[p1].size(); i++)
	{
		pile[p2].push_back(pile[p1][i]);
	}
	pile[p1].resize(h1);
}