积木移动题目分析及解题思路------木块问题(1)
- 一、题目描述
- Input
- Output
- 二、题目分析
-
- [2.1 难度分析](#2.1 难度分析)
- [2.2 C语言识别策略](#2.2 C语言识别策略)
-
- [2.2.1 数据输入方式](#2.2.1 数据输入方式)
- [2.2.2 数组第二维下标识别](#2.2.2 数组第二维下标识别)
- [2.3 积木操作](#2.3 积木操作)
- 三、解题思路
-
- [3.1 基本思路](#3.1 基本思路)
- [3.2 ASCII码值测试](#3.2 ASCII码值测试)
今天在看到《算法竞赛入门经典》例题5-2 木块问题(The Blocks Problem ,Uva 101),书中的题目描述和Uva网站上的要求还是有一些细微差异的。我想在看书本答案之前自己做做这道题,考虑到测试方便,本文题目描述完全基于原题网站。
Uva网站题目链接:
Problem ID: 101:The Blocks Problem
点击箭头处可浏览英文题目描述。

一、题目描述
木块问题(The Blocks Problem,Uva 101):
从左到右有n个木块,编号为0~n-1,要求模拟以下4种操作(下面的a和b都是木块编号)。
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相等或在同一堆时,指令是非法指令,应当忽略(不做任何移动操作)。
Input
第1行输入整数 n n n,表示积木数量 0 < n < 25 0 \lt n \lt 25 0<n<25。
从第2行开始,每行输入一个积木操作命令,直到遇到quit命令为止。
Output
输出每个位置的积木列表,位置编号 i i i满足 0 ≤ i < n 0 \le i \lt n 0≤i<n
每行由"位置编号+冒号+空格+积木列表"构成,积木列表中各个积木编号中间用空格隔开,最后一个积木编号后不能有空格。
每个块位置输出一行,即总共输出n行。
样例输入:
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:
二、题目分析
这个问题算法上没什么难度,就是按人家要求把积木块移来移去就行了。
最直接的想法就是搞个二维数组,第一维表示积木的位置,第二维表示位置上有哪些积木。
2.1 难度分析
题目的难度主要在于识别:
- 识别输入内容。要识别
move 9 onto 1这样的输入属于哪种操作类型,然后才能按要求操作对应的积木。对于C语言来说,那无非就是scanf、getchar、fgets;如果是C++,那就是cin、getline,但是这两个东东俺不太熟,还是想先用C语言搞一搞。 - 数组第二维下标识别。把积木移到某个位置,都要找到放到对应位置的第几层(也就是第二维的下标)。如果是用普通数组那需要自己去识别,但是如果用C++的vector,那就美美不用管它了。
2.2 C语言识别策略
C++可以使用string、vector,两大难点都很容易解决,不用详细讨论,这里只讨论C语言的识别策略。
2.2.1 数据输入方式
用scanf输入的好处是可以自然地以"空格"为界把同一行分为四个部分,清清楚楚,然后就分头处理各个串就行了。但是这需要用到字符串比较函数,说实话这玩艺我还没用过,不过应该很简单,查一下就行了。
如果用getchar呢,那就要一个字符一个字符去识别,读取一次就完事了,貌似也不是不可以。
用fgets整行输入是没什么意义的,因为还得从头一个字符一个字符的去分析,那还不如直接用getchar。
2.2.2 数组第二维下标识别
如果就用C语言的普通数组,我想到两种处理方法:
(1)预设不可能值法。就是把数组所有元素的值预先赋一个不可能取值,比如"-1",这样第一个"-1"就是数组的末尾。如果是动态数组那就简单了。
(2)存储末尾下标法。更简单的方法,就是用数组中某个固定位置(比如第一个或最后一个位置)的元素实时记录数组的最后一个积木的下一个位置,也就是第一个空位。我打算用这个方法。
2.3 积木操作
积木操作类型一种有4种:
- 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所在木块堆的顶部。
总结下来,实际上只有两种操作:
- 归位:把a或b上方的积木放回到原来的位置。
- 移动:把a及上面的木块直接摞到b所在位置处。
可以把这两种操作写成两个函数ret(x),move(a, b)。
归位函数我本来想用return(x),但是return是关键字,只好搞个阉割版。
三、解题思路
我决定先用getchar操练一下,一次读入,搞定识别,不用标准库的其他任何字符串函数。
3.1 基本思路
getchar函数的功能是一次读入一个字符,所以就要每读一个字符,识别一次。读完一行的所有字符,就要识别出操作类型,积木a、b的编号。
识别过程中,一旦读到move就调用ret(a),一旦读到onto就调用ret(b),读到pile、over不用作任何处理。
最后,直接调用move(a, b)就OK了。
c
int main(){
while(flag){
int c=getchar();
if(回车){
识别命令;
执行命令:ret(x);
执行命令:move(a, b);
}
}
}
3.2 ASCII码值测试
忘了数学、字母的ASCII码值了,写了个程序测试了一下。
c
#include<stdio.h>
int main(){
int c;
c=getchar();
printf("%d\n", c);
return 0;
}
由程序得出以下字符的ASCII码值:
- 0:48
- a:97
- 回车:10
- 空格:32
天不早了,今天就先叨叨到这里,下集再写具体的代码。