本题思路上没有什么难点,直接模拟操作便可。
难点在于如何用vector模拟出这些操作。通过这题,可以对vector的用法有所理解。
在框架上,由于不同操作有很多相同之处,可将它们提炼出来,故本题用到很多自定义函数,算是自顶向下的求解。但是由于我的思维不够成熟,共同点的提炼较浅,更简洁的方法参见《算法竞赛入门经典》P110.
//#define LOCAL
#include <cstdio>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
const int maxn=30;
vector <int>pile[maxn];
int N,a,b,pileA,pileB;
int find_pile(int c){ //找到木块c所在堆
for(int i=0;i<N;i++)
for(int j=0;j<pile[i].size();j++)
if(pile[i][j]==c) return i;
}
void get_pileAB(){
pileA=find_pile(a);
pileB=find_pile(b);
}
void recover(int pC,int c){ //将c所在堆c纸上的木块还原,未还原c
int s=pile[pC].size()-1;
int bloc=pile[pC][s];
while((bloc!=c)&&s>=0){
pile[bloc].push_back(bloc);
pile[pC].pop_back();
bloc=pile[pC][--s];
}
}
void move_pile(int p1,int p2,int c){ //将c及以上木块从c所在堆p1移到p2
int height;
for(height=0;height<pile[p1].size();height++)
if(pile[p1][height]==c) break;
for(int i=height;i<pile[p1].size();i++)
pile[p2].push_back(pile[p1][i]);
pile[p1].resize(height);//这条语句的应用避免了在写一重循环,利用pop_back()依次删除
}
void move_a_onto_b(){
recover(pileA,a);
recover(pileB,a);
pile[pileB].push_back(a); //单独处理a
pile[pileA].pop_back();
}
void move_a_over_b(){
recover(pileA,a);
pile[pileB].push_back(a);
pile[pileA].pop_back();
}
void pile_a_onto_b(){
recover(pileB,b);
move_pile(pileA,pileB,a);
}
void pile_a_over_b(){
move_pile(pileA,pileB,a);
}
void print(){ //输出
for(int i=0;i<N;i++){
printf("%d:",i);
if(pile[i].empty()) printf("\n");
else{
for(int j=0;j<pile[i].size();j++)
printf(" %d",pile[i][j]);
printf("\n");
}
}
}
int main(){
#ifdef LOCAL
freopen("data.in","r",stdin);
#endif
scanf("%d",&N);
for(int i=0;i<N;i++) pile[i].push_back(i);
string s1,s2;
while(cin>>s1>>a>>s2>>b){
if(s1=="quit") break; //这条语句的位置有讲究,不能放在下面的那个地方
if(a==b) continue;
get_pileAB();
if(pileA==pileB)continue;
// if(s1=="quit") break; //pileA和B是全局变量,如果上一条语句pileA和pileB相等,接下来是quit,如果放在这里,这quit将不会起到作用。
if((s1=="move")&&(s2=="onto")) move_a_onto_b();
else if((s1=="move")&&(s2=="over")) move_a_over_b();
else if((s1=="pile")&&(s2=="onto")) pile_a_onto_b();
else if((s1=="pile")&&(s2=="over")) pile_a_over_b();
}
print();
return 0;
}
注意点:1.vector不定长数组的用法:
1)定义vector数组:
vector <int>pile[maxn]; //每个pile是一个vector<int>
2)获取vector的长度,直接访问里面的元素(和普通数组一样)
int find_pile(int c){ //找到木块c所在堆
for(int i=0;i<N;i++)
for(int j=0;j<pile[i].size();j++)
if(pile[i][j]==c) return i;
}
3)向最后添加元素,删除最后的元素
pile[pileB].push_back(a);
pile[pileA].pop_back();
这里,通过实验,发现似乎不可以以直接赋值的方式赋值(或者说即使赋值了但不可访问)给还未“开辟”的单元格,但是可以直接访问、修改已经用push_back()开辟的单元格。如下:
int main(){
vector<int>pile;
pile.push_back(1);
pile.push_back(2);
pile[1]=3; //修改已开辟的单元格,修改成功且可以被访问
pile[2]=5; //不用push_back()在末尾添加元素,可赋值但是无法被访问
pile[8]=9; //直接赋值较远的单元格,可赋值但无法被访问
printf("%d %d %d %d\n",pile.empty(), pile[1],pile[2],pile[8]);
for(int i=0;i<pile.size();i++) printf("%d\n",pile[i]);
}
结果:
0 3 5 9
1
3
4)重新改变数组大小
pile[p1].resize(height);
这条语句的应用见上面代码,可用这条语句实现后面一大堆数据不要的功能。
通过实验,resize()可能只是将vector中更改了某些变量,如与size有关的变量,但是并没有消除元素,释放空间。
int main(){
vector<int>pile;
pile.push_back(1);
pile.push_back(2);
pile.push_back(3);
pile.push_back(4);
pile.push_back(5);
pile.resize(3);
printf("%d\n",pile[4]);
}
结果:
5
结合两个实验,可以认为一些元素可以存在但是可能不被vector认可,即在逻辑上已经不是vector的一部分了。
5)判断是否为空
if(pile[i].empty()) printf("\n");
若为空,返回1,否则返回0。
2.输入问题
本题需要输入指令而且需要从指令中提炼出数字信息,一开始的想法是命令长度一样,想定义字符数组
char cmd[13],但是含有空格、quit的与其他的不一致给字符串的输入造成了困难(不知道用哪个函数、怎么用)。
借鉴本题中的输入方法,利用cin和string。
string s1,s2;
while(cin>>s1>>a>>s2>>b){……} //cin可以连续输入到不同变量中
通过接触了getchar()和上面的方式,请意识到并接受:
1)getchar():虽然键盘上是一次性输入的,但是是一个一个字符接收的。
2) 看上去是一个整体的变量(一行字符串等)可以拆分成不同的部分,用不同的变量来储存。