The Blocks Problem,Uva 101 算法竞赛入门经典例题5-2

题目

链接

点此处跳转题目

输入

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:

题解

题目简化

给你n个木块,分别标着0~n-1的标号,进行一系列操作,输出每个位置的木块

思路

这道题挺绕的(不,只要是题我都觉得挺绕的,我以为是我英语没学好,翻译过来又觉得是我语文没学好)

一共有五个操作,让我们来读一下题

(1)读题

• move a onto b
where a and b are block numbers, puts block a onto block b after returning any blocks that are
stacked on top of blocks a and b to their initial positions.
• move a over b
where a and b are block numbers, puts block a onto the top of the stack containing block b, after
returning any blocks that are stacked on top of block a to their initial positions.
• pile a onto b
where a and b are block numbers, moves the pile of blocks consisting of block a, and any blocks
that are stacked above block a, onto block b. All blocks on top of block b are moved to their
initial positions prior to the pile taking place. The blocks stacked above block a retain their order
when moved.
• pile a over b
where a and b are block numbers, puts the pile of blocks consisting of block a, and any blocks
that are stacked above block a, onto the top of the stack containing block b. The blocks stacked
above block a retain their original order when moved.
• quit
terminates manipulations in the block world.

(2)联想

哦天哪,这是什么一团乱码,又是move又是pile又是onto又是over的
好吧,别看这一串英文了,让我们联想一下搬箱子的步骤

move a onto b
a)搬起来 (b上面的箱子)
b)放回最初始的位置
a)搬起来(a上面的箱子)
b)放回最初始的位置
c)把a放到b上

move a over b
a)搬起来(a上面的箱子)
b)放回最初始的位置
c)把a放到b上

pile a onto b
a)搬起来 (b上面的箱子)
b)放回最初始的位置
a)搬起来(a以及a上面的箱子)
c)放到b上

pile a over b
a)搬起来(a以及a上面的箱子)
c)放到b上

可见我们有两个操作 a拿起 b+c放下

(3)拆分

实际联想似乎搬个箱子并不难,只要力气足够大
让我们来再看看题目的前四个操作
s1 s2
move onto
move over
pile onto
pile over

诶?这么说s1只输入了move/pile,s2只输入了onto/over?

让我们来拆分一下!
s1-----
move 只a本身,a上面放回原处
pile a和a上面全部
s2-----
onto 到b本身后面,b上面放回原处
over 到b所在那一列的最后面

也就是说只有move和onto需要多一步将上面的箱子放回原处的操作
pile和over只需要直接移动放下

(4)简化与合并

四个操作要直接硬写也不是不可以,如果你不嫌来回搬箱子累的话

跟我一样力气小的人就再来简化一下,偷个懒吧
我们把操作直观的简述出来之后,我们会发现这一整个整体只有两步操作
那就是拿起放下
俗话说得好,我们要拿得起放得下
对,不管你把箱子放到哪里,或者你拿起来的是什么箱子,归根到底只有拿起放下
那就写一个叫拿起放下的函数(2)吧,具体怎么写等要写了再说
好,让我们开始一起无头苍蝇的乱找箱子拿起放下吧
我们好像忘了去找箱子!!!
那就再写一个名为查找的函数(1),一起完成要的和要的箱子
这样一来,整个框架就完成了!
然后本蒟蒻就翻车了,我拿不起这道题,也放不下

AC代码

(1)查找木块

查找的时候,我们需要输入一下要查找的木块号码,也就是这里的a
找到它之后,我们要返回这个a的位置p,和它处在这个p位置的高度h
这里&p和&h以引用的形式返回调用者

void find(int a, int& p, int& h) {
    
    
	for (p = 0; p < n; p++)
	{
    
    
		for (h = 0; h < d[p].size(); h++) {
    
    
			if (d[p][h]==a) return;
		}
	}
}

(2)拿起放下

p:从p处拿起
h:拿起p处h高度以上的木块
p2:放到p2处 若p2==-1,即放回原处

显然,拿起操作即输入,输入的时候输入的高度(h)和第一个位置( p )
放下操作是该函数的具体内容

void onto(int h, int p,int p2) {
    
    
	for (int i = h; i < d[p].size(); i++)
	{
    
    
		if (p2==-1) d[d[p][i]].push_back(d[p][i]);//放回原处
		else d[p2].push_back(d[p][i]);//放到新处
	}
	d[p].resize(h);//移除拿起的木块
}

蒟蒻的全部AC代码

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;

int n;
vector<int>d[30];
int pa, pb, ha, hb;

void find(int a, int& p, int& h) {
    
    
	for (p = 0; p < n; p++)
	{
    
    
		for (h = 0; h < d[p].size(); h++) {
    
    
			if (d[p][h]==a) return;//找到箱子,返回位置和高度
		}
	}
}


void onto(int h, int p,int p2) {
    
    
	for (int i = h; i < d[p].size(); i++)
	{
    
    
		if (p2==-1) d[d[p][i]].push_back(d[p][i]);//如果p2==-1,也就是令在p处h高度以上的箱子放回原处
		else d[p2].push_back(d[p][i]);//令p处h高度以上的箱子放到新的p2处
	}
	d[p].resize(h);//p处h高度以上的箱子已经被挪走,调整d的大小为0~h,丢弃上面的箱子
}

int main() {
    
    
	cin >> n;
	string s1, s2;
	int a, b;
	for (int i = 0; i < n; i++) d[i].push_back(i);//初始化
	for (;;) {
    
    
		cin >> s1;
		if (s1[0] == 'q') break;//结束询问
		cin >> a >> s2 >> b;
		find(a, pa, ha);
		find(b, pb, hb);
		if (pa == pb) continue;//a和b的位置相等也包含a==b,只需要写pa==pb来一举两得
		if (s1[0]=='m'&&ha+1<d[pa].size()) onto(ha+1, pa,-1);
		 //如果s1操作首字母是m,且a不是在最上面,将上面的放回原处
		if (s2[1]=='n'&&hb + 1 < d[pb].size()) onto(hb + 1, pb,-1);//如果s1操作首字母是n,且b不是在最上面,将上面的放回原处
		onto(ha, pa, pb);//a及a上面的全部挪到b上(如果有将a和b上面的箱子初始化归为操作,已经在上两条代码全部完成)
	}
	for (int i = 0; i < n; i++)//输出操作
	{
    
    
		cout << i << ":";
		for (int j = 0; j < d[i].size(); j++) cout << " " << d[i][j];
		cout << endl;
	}

	return 0;
}

下面让我们来分析一下书中的想法
书中的操作从放到哪的角度考虑,将我合并的放下拆分为了放回原位放到别处两种函数

书上的完整AC代码

#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);
		for(;;){
    
    
			cin >> s1;
			if (s1 == "quit") break;
			cin >> 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;
	}
			

蒟蒻的收获

1、以引用的形式返回调用者

void find(int a, int& p, int& h) {
    
    
	for (p = 0; p < n; p++)
	{
    
    
		for (h = 0; h < d[p].size(); h++) {
    
    
			if (d[p][h]==a) return;//找到箱子,返回位置和高度
		}
	}
}
		find(a, pa, ha);
		find(b, pb, hb);

调用者(pa,ha,pb,ha就都有值了)

2、vector的resize

d[p].resize(h);//p处h高度以上的箱子已经被挪走,调整d的大小为0~h,丢弃上面的箱子

将d的大小调整为0~h

3、分析问题-拆分问题-简化问题-合并问题的思想

详见onto函数,将四个操作合在了一起

4、如果莫名其妙的WA了,就返回去好好读读题

我起初写的是自以为很对的WA代码,然后试图从写这篇博客中找寻自己的错误,果然我写着写着就找到了,WA的原因无非不是看不懂题意理解错题意读错题
题意中的放回最初的位置并非a上面的放回a,b上面的放回b,而是完全初始化,n号箱回n位置

后记

这是蒟蒻第一次写出来比较简单的代码!!!(虽然蒟蒻理解错题意写了很久又想了很久orz)
——蒟蒻的第三篇博客
2020-12-30 2:16

猜你喜欢

转载自blog.csdn.net/m0_52494226/article/details/111939265
今日推荐