题意翻译
初始时从左到右有 n 个木块,编号为 0…n−1,要求实现下列四种操作:
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 在同一堆),无需处理。 - a和b均为木块编号
输出:所有操作输入完毕后,从左到右,从下到上输出每个位置的木块编号
样例输入
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
样例输出
0: 0
1: 1 9 2 4
2:
3: 3
4:
5: 5 8 7 6
6:
7:
8:
9:
思路
观察题目,它给了我们很多重复的单词以及要我们做很多重复的事情
这就是,这道题很重要的一点——操作拆分
拆分细节:move 对应 a 上的木块归位,onto 对应 b 上的木块归位
剩下的便是统一操作:将 a 及其以上的木块,挪到 b 上
由此,我们总共写三个函数代表三种操作即可
//找到木块所在的pile和height
void find_block(int a)
/把p堆高度为h的木块上方的所有木块移回原位
void clear_above(int p,int h)
//把第p堆高度为h及其上方的木块整体移动到p2堆的上方
void pile_onto(int p,int h,int p2)
但在这之前我们需要有一点前置知识,也是我们这道题的另一重点:vector(不定长数组)
vector
vector是一个不定长数组,什么意思呢?就是你可以根据需要,随时改变vector的大小
若a是一个vector,具体为:
a.size():读取 a 的大小,这个在定长数组里也是通用的
a.resize():重新设定vector的大小,这个就是不定长数组特有的功能
a.push_back():向尾部添加元素,可以理解为循环赋值
a.pop_back():删除最后一个元素
声明方法
前面是vector,中间写数据类型,最后是声明的变量名字
vector<int>a vector<string>b
有了这个知识呢,我们就可以开始写具体操作了
函数头和一些声明
#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
using namespace std;
const int maxn=30; //设定最多30个木块
int n; //读取的木块数量
vector<int>pile[maxn]; //每个pile[i]都是一个vector
int ph[2]; //ph为 pile and height, ph[0]存放pile,ph[1]存放height
第一个操作:找到目标木块是在哪
void find_block(int a){
for(int p=0;p<n;p++){ //循环位置
for(int h=0;h<pile[p].size();h++){ //循环高度
if(pile[p][h]==a){ //找到目标木块
ph[0]=p; //记录下位置
ph[1]=h;
}
}
}
}
第二个操作:把目标木块上方的所有木块全都放回原位
void clear_above(int p,int h){
for(int i=h+1;i<pile[p].size();i++){//从目标木块的上方开始找,高度为h+1
int b=pile[p][i];
pile[b].push_back(b); //把木块b放回原位
}
pile[p].resize(h+1); //重新设定少了h上方的木块后,pile[p]的高度
}
第三个操作:第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]); //依次放入pa堆的上方
}
pile[p].resize(h); //重新设定少了h及其上方的木块后,pile[p]的高度
}
用于输出答案的函数
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){
if(s1=="quit") break;
else{
int pa,pb,ha,hb;
cin>>a>>s2>>b;
find_block(a);
pa=ph[0];
ha=ph[1];
find_block(b);
pb=ph[0];
hb=ph[1];
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;
}
总结
以前学习到的主要是函数的复用,没想到在这道题中将指令进行拆分后题目会简单如此多。
总的来说,要学的还很多。
今天学到的新知识:提前各种指令之间的共同点,拆分为操作,这样可以极大地减少重复代码,这在工程代码中或许会起到极大的作用