习题1.1
10.b 欧几里得游戏 一开始,板上写有两个不相等的正整数,两个玩家交替写数字,每一次,当前玩家都必须在板上写出任意两个板上数字的差,而且这两个数字必须是新的,也就是说,不能与板上任何一个已有的数字相同。当玩家再也写不出新数字时,他就输了。请问,你是选择先行动还是后行动呢?
分析:这个游戏和欧几里得算法的减法版本有点类似,所以在黑板上能写上的最小的正整数就是他们的最大公约数。假设最大公约数为m,令两个正整数分别为a = i×m, b = j×m,以a、b开始相减,那么在黑板上写的数也都是m的倍数,最多能写的正整数个数为max(i, j)。
解答:当max(a, b) / gcd(a, b)为奇数,选择先手;若为偶数,选后手。
11 扩展欧几里得算法 不仅能够求出两个正整数m和n的最大公约数d,还能求出两个整数x和y(不一定为正),使得mx+ny=d。
a. 在参考资料中查阅扩展欧几里得算法的描述(参见[KnuI]),然后任选一种语言实现它。
b. 改写上述程序以对丢番图方程ax+by=c求解,系数a,b,c为任意整数。
解答:a.参考文献《计算机程序设计艺术》给出的算法如下:
算法E (推广的欧几里得算法). 给定两个正整数m和n,计算他们的最大公因数d,并计算两个未必为正数的整数 a 和 b, 使得 am+bn=d.
E1. [初始化.] 置 a′←b←1,a←b′←0,c←m,d←n.
E2. [除法.] 令 q 和 r 分别是用 d 除 c 所得的商和余数. (我们有 c=qd+r 和 0≤r <d.)
E3. [余数为0?] 如果 r=0 ,算法终止,此时有 am+bn=d , 正如所求.
E4. [循环.] 置 c←d, d←r, t←a′, a′←a, a←t−qa, t←b′, b′←b, b←t−qb ,然后返回 E2.
C语言实现:
int exgcd(int m, int n, int &x, int &y){
if (n == 0) {
x = 1;
y = 0;
return m;
}
int a, a1, b, b1, c, d, q, r, t;
a1 = b = 1;
a = b1 = 0;
c = m;
d = n;
q = c / d;
r = c%d;
while (r) {
c = d;
d = r;
t = a1;
a1 = a;
a = t - q * a;
t = b1; b1 = b;
b = t - q * b;
q = c / d;
r = c%d;
}
x = a;
y = b;
return d;
}
b. 求方程ax + by = c的解。扩展欧几里德算法求得的x1,y1是ax + by = gcd(a,b)中x和y的一组解。
如果要求ax + by = c的解,令a*x1* z+ b*y1 * z = c * z = gcd(a,b),则z = c / gcd(a,b)。
所以x = x1 * z = x1 * c / gcd(a,b),y = y1 * z = y1 * c / gcd(a,b)。
bool linear_equation(int a, int b, int c, int &x, int &y)
{
int d = exgcd(a, b, x, y);
if (c%d)
return false;
int k = c / d;
x *= k;
y *= k; //求得的只是其中一组解
return true;
}
x、y的解集为:x = x1 + b / gcd(a,b) * t,y = y1 – a / gcd(a,b) * t。(t 为任意值)
12 带锁的门 在走廊上有n个带锁的门,从1到n依次编号。最初所有的门都是关着的。我们从门前经过n次,每一次都从1号门开始。在第i次经过时(i=1,2,……n)我们改变i的整数倍号锁的状态;如果门是关的,就打开它;如果门是打开的,就关上它。在最后一次经过后,哪些门是打开的,哪些门是关上的?有多少打开的门?
分析:对某扇门k而言,从i=1到n遍历,其状态的改变当且仅当i能整除k,那么k/i也能整除k,这样的数总是成对出现的,除非i=k/i,即i*i=k,k是一个完全平方数,那么这扇门最后的状态是打开的。
解答:当门号为完全平方数,这扇门是开着的;否则关闭。打开的门共有 扇。
习题1.2
1 古代谜题 一个农夫带着一只狼、一只羊和一棵白菜来到河边。他需要用船把它们带到河对岸。然而,这艘船只能容下农夫本人和另外一样东西(要么是狼,要么是羊,要么是白菜)。如果农夫不在场的话,狼就会吃掉羊,羊也会吃掉白菜。请为农夫解决这个问题,或者证明它无解。
解答:
(农、狼、羊、菜)===( )
(狼、菜 )===( 农、羊 )
(农、狼、菜 )===( 羊 )
(菜 )===( 农、狼、羊 )
(农、羊、菜 )===( 狼 )
(羊 )===( 农、狼、菜 )
(农、羊 )===( 狼、菜 )
( )===( 农、狼、羊、菜 )
此题狼和菜的运送顺序可交换
2 现代谜题 有4个人打算过桥,他们都在桥的某一端。我们有17分钟让他们全部到达大桥的另一头。时间是晚上,他们只有一只手电筒。一次最多只能有两个人同时过桥,而且必须携带手电筒。必须步行将手电筒带来带去,即扔来扔去是不行的。每个人走路的速度不同:甲过桥要1分钟,乙要用2分钟,丙要用5分钟,丁要用10分钟。两个人一起走的速度等于其中走得慢的那个人的速度。
分析:若每次都让甲带回手电筒,带回时间最短,但一共要花费10+1+5+1+2=19分钟。由于丙和丁花费时间最多,为了节省时间,丙和丁应该一同过桥,然后由甲或乙将手电筒带回。
解答:
(甲、乙、丙、丁)===( )
(丙、丁 )===( 甲、乙 )2分钟
(乙、丙、丁 )===( 甲 )2分钟
(乙 )===( 甲、丙、丁 )10分钟
(甲、乙 )===( 丙、丁 )1分钟
( )===( 甲、乙、丙、丁 )2分钟
此题甲和乙的过桥顺序可交换
9 考虑下面这个算法,他求的是数值数组中大小最接近的两个元素的差。
算法 MinDistance(A[0…n-1])
//输入:数字数组A[0…n-1]
//输出:数组中两个大小相差最少的元素的差值
dmin←∞
for i←0 to n-1 do
for j←0 to n-1 do
if i≠j and |A[i]-A[j]|<dmin
dmin← |A[i]-A[j]|
return dmin
尽可能改进该算法。
解答:
算法 MinDistance2(A[0…n-1])
//输入:数字数组A[0…n-1]
//输出:数组中两个大小相差最少的元素的差值
dmin←∞
for i←0 to n-1 do
for j←i+1 to n-1 do
if |A[i]-A[j]|<dmin
dmin←|A[i]-A[j]|
return dmin
10 匈裔美籍数学家乔治·波利亚写了一本书,名为《怎样解题:数学思维的新方法》,这是关于问题求解的最有影响的书籍之一。波利亚将他的观点总结为4点。请到网上查找这段话,或者最好直接在他的书中找,然后将他的思想和我们在1.2节中概括的方法相比较,看看它们有什么共同之处,有什么不同之处。
解答:波利亚的四步解题法
第一步:你必须弄清问题。
1.已知是什么?未知是什么?要确定未知数,条件是否充分?
2.画张图,将已知标上。
3.引入适当的符号。
4.把条件的各个部分分开。
第二步:形成解决思路。
1.你能否转化成一个相似的、熟悉的问题?
2.你能否用自己的语言重新叙述这个问题?
3.回到定义去。
4.你能否解决问题的一部分?
5.你是否利用了所有的条件?
第三步:实现计划。
1.勇敢地写出你的方法。
2.你能否说出你所写的每一步的理由?
第四步:回顾。
1.你能否一眼就看出结论?
2.你能否用别的方法导出这个结论?
3.你能否把这个题目或这种方法用于解决其他的问题?
共同之处在于首先都要理解问题,然后看是否能转化问题,用现有的算法技术解决问题,接着实现计划,在算法求解时具体为算法描述、正确性证明、分析和写代码。
不同之处在于在算法设计时比解题还要考虑更多,比如了解计算设备性能、选择精确解法还是近似解法、确定数据结构等等。而解题四步骤中的最后一步为总结,在算法问题求解时则没有最后这一步,相似的工作放在了算法分析之中。
习题1.3
4 七桥问题 大家公认,图论诞生于七桥问题。出生于瑞士的伟大数学家欧拉解决了这个问题。该问题如下:一个人是否可能在一次步行中穿越柯尼斯堡城中全部的七座桥后回到起点,且每座桥只经过一次。下面是河以及河上的两个岛和七座桥的草图。
a. 用图的语言定义该问题。
b. 该问题有解吗?如果认为有解,请画出步行路线图;如果认为无解,解释一下原因并指出,为了使这种步行路线成为可能,我们至少需要增加几座新桥。
解答:a.
b.这个问题无解。欧拉认为,除了起点之外,每次当一个人由一座桥进入一块陆地时,他同时也得从另一座桥离开此地。所以每经过一点时,走过两座。从起点离开和最后回到起点也走过两座桥,所以每一个陆地与其他陆地连接的桥数必为偶数。
七桥所形成的图形中,每点所连的桥的数目均为奇数,因此该问题是无解的。为了达到每点桥数均为偶数,最少需要增加两座新桥。
5. 环游世界游戏 在欧拉的发现一个世纪以后,著名的爱尔兰数学家威廉·哈密顿创造了另一个著名的问题,它被称为环游世界游戏。这个游戏是在一块圆形的木板上玩的,板上刻的图如下所示:
寻找哈密顿回路——在回到起点之前,这一路径能够访问该图的所有顶点并且只访问一次。
解答:
8 考虑以下地图。
a. 解释一下如何根据图填色问题对该地图着色,使得相邻区域颜色不同。
b. 利用a 的答案,用最少种类的颜色对该地图着色。
解答:a.图着色问题为:给定一个无向图G=(V, E),其中V为顶点集合,E为边集合,图着色问题即为将V分为K个颜色组,每个组形成一个独立集,即其中没有相邻的顶点。此题对应的无向图为:
b.
对该问题进行模拟涂色,至少需要四种颜色。
9 为以下问题设计一个算法:对于x-y座标平面上的n个点的集合,判断它们是不是都落在同一条圆周线上。
解答:若n=2,返回“是”;若n>=3,选取其中的3个点a、b、c,分别计算线段ab,线段ac的中垂线方程。然后计算两直线方程的交点,若交点不存在,则返回“否”;若交点存在,判断a和剩余的每个点距离该交点是否一样长,若一样,返回“是”;否则返回“否”。
习题1.4
10 设计一个检查两个单词是否为变位词的算法,也就是说,是不是能够通过改变一个单词的字母顺序,来得到另一个单词。例如单词tea和eat是变位词。
解答:首先检验两个单词长度一致但是不相等,若判断结果为“否”,直接返回“否”。然后用一个数组记录各个字母的出现次数,用H(i)分别对应字母a~z、A~Z出现的次数,i=0,1,2…51。首先遍历第一个单词中的字母,H(i)相应位置数目加一。然后遍历另一个单词中的字母,H(i)相应位置数目减一,一旦发现有H(i)中的数小于零,返回“否”;遍历结束,返回“是”。