算法竞赛入门经典
开始接触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 上。
如此变换,回头看题发现有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种指令的抽象总结。
总结:大佬太多,菜鸡我要坚持啊。
本人喜欢批评指正!
有不对的地方一定要告诉我啊!