熱門文章

2026年2月15日 星期日

ZeroJudge 解題筆記:c073. 00101 - The Blocks Problem

作者:王一哲
日期:2026年2月15日


ZeroJudge 題目連結:c073. 00101 - The Blocks Problem

解題想法


指令有以下 5 種,其中 a 和 b 是積木的編號:
  1. move a onto b: 先將 a 和 b 上方的積木放回原來的位置,再將 a 移到 b 上方。
  2. move a over b: 先將 a 上方的積木放回原來的位罝,b 所在的那堆積木不動,再將 a 移到 b 上方。
  3. pile a onto b: 先將 b 上方的積木放回原位,再將 a 本身及上方的積木一起放到 b 上方。
  4. pile a over b: 將 a 本身和上方的積木一起搬到到 b 所在的那堆積木上方。
  5. quit: 動作結束,印出結果。
先畫出範例測資 1 的操作過程再寫程式碼,會比較清楚要怎麼寫。範例測資 1,$n = 10$,初始及每次操作後的狀態分別為
initial: [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]
move 9 onto 1: [[0], [1, 9], [2], [3], [4], [5], [6], [7], [8], []]
move 8 over 1: [[0], [1, 9, 8], [2], [3], [4], [5], [6], [7], [], []]
move 7 over 1: [[0], [1, 9, 8, 7], [2], [3], [4], [5], [6], [], [], []]
move 6 over 1: [[0], [1, 9, 8, 7, 6], [2], [3], [4], [5], [], [], [], []]
pile 8 over 6: [[0], [1, 9, 8, 7, 6], [2], [3], [4], [5], [], [], [], []]  # 8, 6 在同一疊,無效
pile 8 over 5: [[0], [1, 9], [2], [3], [4], [5, 8, 7, 6], [], [], [], []]
move 2 over 1: [[0], [1, 9, 2], [], [3], [4], [5, 8, 7, 6], [], [], [], []]
move 4 over 9: [[0], [1, 9, 2, 4], [], [3], [], [5, 8, 7, 6], [], [], [], []]

範例測資 2,$n = 4$,初始及每次操作後的狀態分別為
initial: [[0], [1], [2], [3]]
pile 0 over 1: [[], [1, 0], [2], [3]]
pile 2 over 3: [[], [1, 0], [], [3, 2]]
move 1 onto 3: [[0], [], [2], [3, 1]]


Python 程式碼


使用時間約為 7 ms,記憶體約為 3.1 MB,通過測試。
import sys

for line in sys.stdin:
    n = int(line)
    piles = [[i] for i in range(n)]  # 每一疊的木塊編號
    pos = [i for i in range(n)]  # 每個編號的木塊各在第幾疊
    for line in sys.stdin:
        if line.rstrip() == "quit":
            for i, pile in enumerate(piles):
                print(f"{i:d}: ", end="")
                print(*pile)
            break
        command = list(line.split())
        a, b = int(command[1]), int(command[3])
        if pos[a] == pos[b]: continue  # a, b 在同一疊,不合法的指令
        if command[0] == "move":
            if command[2] == "onto":
                while piles[pos[a]][-1] != a:  # 檢查 a 所在的那一疊上方
                    c = piles[pos[a]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                    piles[c].append(c)
                while piles[pos[b]][-1] != b:  # 檢查 b 所在的那一疊上方
                    c = piles[pos[b]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                    piles[c].append(c)
                piles[pos[b]].append(piles[pos[a]].pop())  # 將 a 移到 b 所在那一疊上方
                pos[a] = pos[b]  # a 的位置改成 b 所在的位置
            elif command[2] == "over":
                while piles[pos[a]][-1] != a:  # 檢查 a 所在的那一疊上方
                    c = piles[pos[a]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                    piles[c].append(c)
                piles[pos[b]].append(piles[pos[a]].pop())  # 將 a 移到 b 所在那一疊上方
                pos[a] = pos[b]  # a 的位置改成 b 所在的位置
        elif command[0] == "pile":
            if command[2] == "onto":
                while piles[pos[b]][-1] != b:  # 檢查 b 所在的那一疊上方
                    c = piles[pos[b]].pop()  # 取出並移除最上方的木塊編號為 c
                    pos[c] = c  # 把 c 歸位
                idx = piles[pos[a]].index(a)  # 找出 a 在這一疊的索引值
                sub = piles[pos[a]][idx:]  # 取出 idx 之後的項目
                piles[pos[a]] = piles[pos[a]][:idx]  # 移除 idx 之後的項目
                for d in sub:  # 修改 a 及上方積木的位置
                    pos[d] = pos[b]  # 改成 b 所在的位置
                piles[pos[b]] += sub  # 將 a 及上方的積木移到 b 所在那一疊上方
            elif command[2] == "over":
                idx = piles[pos[a]].index(a)  # 找出 a 在這一疊的索引值
                sub = piles[pos[a]][idx:]  # 取出 idx 之後的項目
                piles[pos[a]] = piles[pos[a]][:idx]  # 移除 idx 之後的項目
                for d in sub:  # 修改 a 及上方積木的位置
                    pos[d] = pos[b]  # 改成 b 所在的位置
                piles[pos[b]] += sub  # 將 a 及上方的積木移到 b 所在那一疊上方


C++ 程式碼


使用時間約為 1 ms,記憶體約為 328 kB,通過測試。
#include <iostream>
#include <vector>
#include <string>
using namespace std;

int main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int n;
    while(cin >> n) {
        vector<vector<int>> piles (n, vector<int> (1));  // 每一疊的木塊編號
        vector<int> pos (n);  // 每個編號的木塊各在第幾疊
        for(int i=0; i<n; i++) {
            piles[i][0] = i;
            pos[i] = i;
        }
        string cmd1, cmd2;
        int a, b;
        while(cin >> cmd1) {
            if (cmd1 == "quit") {
                for(int i=0; i<n; i++) {
                    cout << i << ": ";
                    if (piles[i].empty()) {
                        cout << "\n";
                        continue;
                    }
                    for(int j=0; j<(int)piles[i].size(); j++) {
                        cout << piles[i][j] << " \n"[j == (int)piles[i].size()-1];
                    }
                }
                break;
            }
            cin >> a >> cmd2 >> b;
            if (pos[a] == pos[b]) continue;  // a, b 在同一疊,不合法的指令
            if (cmd1 == "move") {
                if (cmd2 == "onto") {
                    while(piles[pos[a]].back() != a) {  // 檢查 a 所在的那一疊上方
                        int c = piles[pos[a]].back();  // 取出並移除最上方的木塊編號為 c
                        piles[pos[a]].pop_back();
                        pos[c] = c;  // 把 c 歸位
                        piles[c].push_back(c);
                    }
                    while(piles[pos[b]].back() != b) {  // 檢查 b 所在的那一疊上方
                        int c = piles[pos[b]].back();  // 取出並移除最上方的木塊編號為 c
                        piles[pos[b]].pop_back();
                        pos[c] = c;  // 把 c 歸位
                        piles[c].push_back(c);
                    }
                    piles[pos[b]].push_back(piles[pos[a]].back());  // 將 a 移到 b 所在那一疊上方
                    piles[pos[a]].pop_back();
                    pos[a] = pos[b];  // a 的位置改成 b 所在的位置
                } else if (cmd2 == "over") {
                    while(piles[pos[a]].back() != a) {  // 檢查 a 所在的那一疊上方
                        int c = piles[pos[a]].back();  // 取出並移除最上方的木塊編號為 c
                        piles[pos[a]].pop_back();
                        pos[c] = c;  // 把 c 歸位
                        piles[c].push_back(c);
                    }
                    piles[pos[b]].push_back(piles[pos[a]].back());  // 將 a 移到 b 所在那一疊上方
                    piles[pos[a]].pop_back();
                    pos[a] = pos[b];  // a 的位置改成 b 所在的位置
                }
            } else if (cmd1 == "pile") {
                if (cmd2 == "onto") {
                    while(piles[pos[b]].back() != b) {  // 檢查 b 所在的那一疊上方
                        int c = piles[pos[b]].back();  // 取出並移除最上方的木塊編號為 c
                        piles[pos[b]].pop_back();
                        pos[c] = c;  // 把 c 歸位
                    }
                    vector<int> tmp;  // 暫存從 a 所在這疊從最上面到 a 為止的資料
                    while(piles[pos[a]].back() != a) {
                        int c = piles[pos[a]].back();
                        piles[pos[a]].pop_back();
                        tmp.push_back(c);
                    }
                    tmp.push_back(piles[pos[a]].back());  // 最後要再處理一次,這項是 a
                    piles[pos[a]].pop_back();
                    while(!tmp.empty()) {  // 從 tmp 最後面取出資料移到 b 所在那疊
                        int c = tmp.back();
                        tmp.pop_back();
                        piles[pos[b]].push_back(c);
                        pos[c] = pos[b];
                    }
                } else if (cmd2 == "over") {
                    vector<int> tmp;  // 暫存從 a 所在這疊從最上面到 a 為止的資料
                    while(piles[pos[a]].back() != a) {
                        int c = piles[pos[a]].back();
                        piles[pos[a]].pop_back();
                        tmp.push_back(c);
                    }
                    tmp.push_back(piles[pos[a]].back());  // 最後要再處理一次,這項是 a
                    piles[pos[a]].pop_back();
                    while(!tmp.empty()) {  // 從 tmp 最後面取出資料移到 b 所在那疊
                        int c = tmp.back();
                        tmp.pop_back();
                        piles[pos[b]].push_back(c);
                        pos[c] = pos[b];
                    }
                }
            }
        }
    }
    return 0;
}


沒有留言:

張貼留言