刷题 | 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;
}

代码中有许多值得学习的地方。

  1. C++中一般使用const int定义常数,而不是C中的#define
const int maxn = 30;
vector<int> pile[maxn];
  1. 以引用的形式返回调用者,可以实现一个函数返回多个参数。
//找木块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;
			}
		}
	}
}
  1. 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的元素
}
  1. C++中的string变量可以直接使用==进行比对
if (s2 == "onto") {
	clear_above(pb, hb);
}
if (s1 == "move") {
	clear_above(pa, ha);
}
  1. 反复读入某一格式的输入条目,直至结束的写法:
int a, b;
string s1, s2;
while (cin >> s1 >> a >> s2 >> b) {
	......
}
posted @ 2021-01-15 15:19  杞棠  阅读(296)  评论(0编辑  收藏  举报