[ 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);
}