题目描述
关于上面的指令,说明如下:
- move a onto b: a 和 b 都是木块号码,在把堆放在木块 a 和 b 上的所有木块归位到它们的初始位置后,再把木块 a 放到木块 b 上。
- move a over b: a 和 b 都是木块号码,在把堆放在木块 a 上的所有木块归位到它们的初始位置后,再把木块 a 放到包含木块 b 的木块堆上。
- pile a onto b: a 和 b 都是木块号码,把 b 上方的木块全部归位,然后把 a 及上面的木块整体摞在 b 上面。
- pile a over b: 木块 a 和 b 都是木块号码,把 a 及上面的木块整体摞在 b 所在的木块堆的顶部。
- quit: 终止操作。
根据上面的样例输入,制作出如下的移动过程表,用来辅助理解。注意,下面的指令中,pile 8 over 6 是非法指令,根据上面的题目描述,我们只需将其忽略即可。
![20201229142626](E:VSCodeWorkspaceworkspace博客园算法竞赛入门经典读书笔记UVa 101.assets20201229142626.png)
因为原紫书给出的样例很简陋,所以,我参照了原题的样例,给出了上面的动态移动过程模拟,这样题目的意思应该就一目了然了。
代码
#include<cstdio>
#include<string>
#include<vector>
#include<iostream>
using namespace std;
const int maxn = 30; // 题目中给定的 n 范围:0 < n < 25
int n; // 在主程序中手动输入
vector<int> pile[maxn]; // 每个 pile[i] 是一个 vector
// 找木块 a 所在的 pile 和 height,以引用的方式返回调用者
void find_block(int a, int& p, int& h)
{
for (p = 0; p < n; p++)
{
for (h = 0; h < pile[p].size(); h++)
if (pile[p][h] == a) return;
}
}
// 把第 p 堆高度为 h 的木块上方的所有木块移回原位
void clear_above(int p, int h)
{
for (int i = h + 1; i < pile[p].size(); i++)
{
int b = pile[p][i];
pile[b].push_back(b); // 把木块 b 放回原位
}
pile[p].resize(h + 1); // pile 只应该保留下标 0 - h 的元素
}
// 把第 p 堆高度为 h 及其上方的木块整体移动到 p2 堆的顶部,书中函数命名为 pile_onto,我认为 Pile_over 更合适一点
void pile_over(int p, int h, int p2)
{
for (int i = h; i < pile[p].size(); i++)
pile[p2].push_back(pile[p][i]);
pile[p].resize(h); // pile 只保留 0 到 h - 1 的元素
}
void print()
{
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("
");
}
}
int main()
{
int a, b;
cin >> n;
string s1, s2;
for (int i = 0; i < n; i++)
pile[i].push_back(i);
while (cin >> s1)
{
if (s1 == "quit") break;
cin >> a >> s2 >> b;
int pa, pb, ha, hb;
find_block(a, pa, ha); // 找到 a 所在的堆 pa,和高度 ha
find_block(b, pb, hb); // b
if (pa == pb) continue; // 如果 a 和 b 在同一个堆,那么,本指令是非法指令,忽略
if (s2 == "onto") clear_above(pb, hb);
if (s1 == "move") clear_above(pa, ha);
pile_over(pa, ha, pb);
}
print();
return 0;
}
输出结果:
注意,紫书原来的代码没有考虑 quit 这一条指令,上面的代码是修改版。