刷题 | C++ | 算法竞赛入门经典 5-2木块问题
题目描述如下:
从左到右有n个木块,编号为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所在木块堆的顶部。
所有操作结束后,输出每个位置的木块列表,按照从底部到顶部的顺序排列。
首先能做的,是整理题目要求,合并同类的操作。
观察发现:当动作为move,需要把a上方的木块全部归位;当介词为onto,需要把b上方的木块全部归位。完成归位动作后,a或b上方的木块可能都已被清空,4种操作其实都需要完成“把a及上面的木块整体摞到b所在木块堆的顶部”。
书本代码如下:
#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 30;
int n;
vector<int> pile[maxn];
//找木块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堆的顶部
void pile_onto(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);
}
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("\n");
}
}
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 >> a >> s2 >> b) {
int pa, pb, ha, hb;
find_block(a, pa, ha);
find_block(b, pb, hb);
if (pa == pb) {
//非法指令
continue;
}
if (s2 == "onto") {
clear_above(pb, hb);
}
if (s1 == "move") {
clear_above(pa, ha);
}
pile_onto(pa, ha, hb);
}
print();
return 0;
}
代码中有许多值得学习的地方。
- C++中一般使用
const int
定义常数,而不是C中的#define
const int maxn = 30;
vector<int> pile[maxn];
- 以引用的形式返回调用者,可以实现一个函数返回多个参数。
//找木块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;
}
}
}
}
- vector可以使用
resize(n)
函数使vector只保留前n个元素(也就是下标0~n-1的元素),不需要使用pop_back()
反复删除最后一个元素
//把第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的元素
}
- C++中的string变量可以直接使用
==
进行比对
if (s2 == "onto") {
clear_above(pb, hb);
}
if (s1 == "move") {
clear_above(pa, ha);
}
- 反复读入某一格式的输入条目,直至结束的写法:
int a, b;
string s1, s2;
while (cin >> s1 >> a >> s2 >> b) {
......
}