紫书P110;vector的应用;UVa101 The Blocks Problem
题目大意:
输入n (0<n<25),得到编号为0到n-1的木块,分别摆放在顺序排列编号为0到n-1的位置。现对这些木块进行操作,操作分为四种。
- move a onto b:把木块a、b上的木块放回各自的原位,再把a放到b上;
- move a over b:把a上的木块放回各自的原位,再把a发到含b的堆上;
- pile a onto b:把b上的木块放回各自的原位,再把a连同a上的木块移到b上;
- pile a over b:把a连同a上木块移到含b的堆上。
当输入quit时,结束操作并输出0~n-1的位置上的木块情况。
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:
题目分析(思路依据紫书):
数据结构选择:每个单元格内block的数目是动态的,固可以采用vector模拟一个单元格,而单元格总共有n个,n值确定,所以可以开一个每个单元都是vector的数组:vector<int> pile[26]
算法设计:题目涉及整段数据与单个数据的移动,且n最大只有25,固采用模拟的方法时间复杂度完全够用。
而模拟的过程涉及两个步骤:<1. 复原block <2. 移动block;
分析题目中四条指令:
move需要将a上方block全部复原,单独移动a;
pile不需要将复原a上方block,需要整段移动;
onto需要将b上方block全部复原,要求a与b相邻;
over不需要复原b上方block,再移动a或者整段。
分析四条指令的异同点且结合先复原后移动的顺序,刘汝佳老师巧妙地进行了以下处理(个别地方与原代码不同):
//pa和pb为a和b的数组下标,ha和hb为a和b的vector下标
if(order1=="move") clear_above(pa,ha);//将pa,ha位置上方的block复原
if(order2=="onto") clera_above(pb,hb);//将pb,hb位置上方的block复原
pile_onto(pa,ha,pb);//将pa,ha位置及其上方的block移动至pb顶端
模块设计:至此,程序的基本框架为:预处理–输入与初始化–模拟–输出–return 0;
。
代码:
#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int n,a,b;
string order1,order2;
vector<int> pile[26];
void find_block(int num,int& h,int& p);
void clear_above(int h,int p);
void pile_onto(int p1,int h1,int p2);
void print();
int main()
{
//读入与初始化
scanf("%d",&n);
for(int i=0;i<n;i++) pile[i].push_back(i);
//模拟
while(cin>>order1>>a>>order2>>b)
{
int ha,hb,pa,pb;
find_block(a,ha,pa);
find_block(b,hb,pb);
if(pa==pb) continue;
if(order1=="move") clear_above(ha,pa);
if(order2=="onto") clear_above(hb,pb);
pile_onto(pa,ha,pb);
//print();
}
//输出
print();
return 0;
}
void find_block(int num,int& h,int& p)
{
for(p=0;p<n;p++)
for(h=0;h<pile[p].size();h++)
if(pile[p][h]==num) return ;
}
void clear_above(int h,int p)
{
for(int i=h+1;i<pile[p].size();i++)
pile[pile[p][i]].push_back(pile[p][i]);
pile[p].resize(h+1);
return ;
}
void pile_onto(int p1,int h1,int p2)
{
for(int i=h1;i<pile[p1].size();i++)
pile[p2].push_back(pile[p1][i]);
pile[p1].resize(h1);
return ;
}
void print()
{
for(int i1=0;i1<n;i1++)
{
printf("%d:",i1);
for(int i2=0;i2<pile[i1].size();i2++) printf(" %d",pile[i1][i2]);
printf("\n");
}
}
要点与细节总结:
- 刘汝佳老师在计算a块与b块所在的h与p时,采用了引用传值的方法,一个函数同时计算了两个局部变量:
void find_block(int num,int& h,int& p)
- 数据结构的选择vector数组,题目中使用到的函数有
v.size();
v.resize();
v.push_back();
- 提取四个指令的共同点,设计出两个函数减少重复代码。