Uva 101:木块问题 The Blocks Problem(详细说明)+(难点)

算法竞赛入门经典

开始接触ACM,老师推荐了紫皮书,但是C++渣渣的我读题都很费劲,搜罗博主文章发现几乎都是 原题+代码 。现尽己所能整理,比较啰(xiang)嗦(xi),希望能对同起步小白有所帮助,嘻嘻。

STL初步:不定长数组:vector

什么是vector?

向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

原题目

从左到右有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所在木块堆的顶部。

遇到quit时终止一组数据。a和b在同一堆的指令是非法指令,应当忽略。

Sample 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
Sample Output
0: 0
1: 1 9 2 4
2:
3: 3
4:
5: 5 8 7 6
6:
7:
8:
9:
————————————————

【题目解释】

第一步:循规蹈矩的把木块一个一个的放在其相应位置上,一萝卜一坑儿。
在这里插入图片描述
第二步:开始变换位置
这里把3放到了2上    5,8 放到了6 上
这里把3放到了2上 5,8 放到了6 上。
如此变换,回头看题发现有5中操作:

  • move
  • onto
  • over
  • pile:一挪挪一堆
  • 归位:放回他自己的坑儿里。(仅一个)
  • 堆:题目中所说的堆,有可能指一个木块,也有可能指一堆木块。

【 分析】

每个木块堆的高度(木块个数)不确定,所以用vector来保存很合适;而木块堆的个数不超过n,所以用一个数组来存就可以了。

代码及解释

main函数(代码极简)

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 30;
int n;
vector<int> pile[maxn]; //每个pile[i]是一个vector(仔细理解!)

int main() {
	int a, b;
	cin >> n;
	string s1, s2;//4中操作里的两种如 move onto
	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, pb);//调用函数③
		}
	print();//调用函数④
	return 0;
}

注意观察:变量及其类型,函数及其参数。代码简洁,没有多余{},所以注意函数调用顺序及优先级。
我们发现为何4中操作却只有两个if语句?↓↓↓↓

哦谢特,作者太聪明了!

“上述代码还有一个值得学习的技巧:输入一共有4种指令,但如果完全独立地处理各指令,代码就会变得冗长而且易错。更好的方法是提取出指令之间的共同点,编写函数以减少重复代码。”—聪明的作者

函数①:find_block(a, pa, ha);

//找木块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;
}

找木块a所在的pile 和height:是指找出a或b的位置
以引用的形式返回调用者:这里的引用是指传进来的参数是直接可操作的。详情百度:c++中引用传递与值传递的区别;
在这里插入图片描述

函数② clear_above(pb, hb)

//把第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_onto(pa, ha, pb);

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

因为对vector不熟,这里我还有个疑点

pile[p2].push_back(pile[p][i]);

把pile[p][i]添加进pile[p2]中,原元素会消失吗?难道.push_back()这个方法是对数据ctrl+x而不是ctrl+c??(手动滑稽)

函数④

嘿嘿,不解释了,脖子疼。。。

总代码

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 30;
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 堆的顶部
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, pb);
}
print();
return 0;
}

小结

数据结构的核心是vectorpile[maxn],所有操作都是围绕它进行的。vector就像一个二维数组,只是第一维的大小是固定的(不超过maxn),但第二维的大小不固定。
vector头文件中的vector是一个不定长数组,可以用clear( )清空,resize( )
改变大小,用push_back( )和pop_back( )在尾部添加和删除元素,用empty( )测试是否为空。
以下是用法。

1.push_back 在数组的最后添加一个数据

2.pop_back 去掉数组的最后一个数据

3.at 得到编号位置的数据

4.begin 得到数组头的指针

5.end 得到数组的最后一个单元+1的指针

6.front 得到数组头的引用

7.back 得到数组的最后一个单元的引用

8.max_size 得到vector最大可以是多大

9.capacity 当前vector分配的大小

10.size 当前使用数据的大小

11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值

12.reserve 改变当前vecotr所分配空间的大小

13.erase 删除指针指向的数据项

14.clear 清空当前的vector

15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)

16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)

17.empty 判断vector是否为空

18.swap 与另一个vector交换数据

难点及总结

比较难理解的是各个函数体中对vector的操作,对4种指令的抽象总结。
总结:大佬太多,菜鸡我要坚持啊。

本人喜欢批评指正!

有不对的地方一定要告诉我啊!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页