一篇让你快速读懂——木块问题(The Blocks Problem)

题意翻译

初始时从左到右有 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;
}

总结

以前学习到的主要是函数的复用,没想到在这道题中将指令进行拆分后题目会简单如此多。

总的来说,要学的还很多。

今天学到的新知识:提前各种指令之间的共同点,拆分为操作,这样可以极大地减少重复代码,这在工程代码中或许会起到极大的作用

 洛谷链接:https://www.luogu.com.cn/problem/UVA101

UVA镜像提交链接:https://vjudge.csgrandeur.cn/problem/UVA-101

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值