算法分析与设计实验指导书.docx
《算法分析与设计实验指导书.docx》由会员分享,可在线阅读,更多相关《算法分析与设计实验指导书.docx(18页珍藏版)》请在冰豆网上搜索。
算法分析与设计实验指导书
《算法分析与设计》实验指导书
姓名:
_____________
学号:
_____________
班级:
_____________
指导教师:
_____________
计算机科学与工程系编
2012年9月6日
使用本书前,请先阅读以下内容:
本书是为配合《算法分析与设计实验教学大纲》而编写的上机指导,其目的是使学生消化理论知识,加深对讲授内容的理解,尤其是一些算法的实现及其应用,培养学生独立编程和调试程序的能力,使学生对算法的分析与设计有更深刻的认识。
上机实验一般应包括以下几个步骤:
(1)准备好上机所需的程序。
手编程序应书写整齐,并经人工检查无误后才能上机。
(2)上机输入和调试自己所编的程序。
一人一组,独立上机调试,上机时出现的问题,最好独立解决。
(3)上机结束后,整理出实验报告。
实验报告应包括:
题目、程序清单、运行结果、对运行情况所作的分析。
本书共分5个实验,其具体要求和步骤如下:
实验一递归与分治
一、实验目的与要求
1、掌握递归方法解决问题的一般技巧;
2、熟悉二分搜索等算法;
3、掌握分治算法思想;
二、实验题
1、设a[0:
n-1]是一个已排好序的数组。
请改写二分搜索算法,使得当搜索元素x不在数组中时,返回小于x的最大元素的位置i和大于x的最小元素位置j。
当搜索元素在数组中时,i和j相同,均为x在数组中的位置。
2、设有n个不同的整数排好序后存放于T[0:
n-1]中,若存在一个下标i,0≤i<n,使得T[i]=i,设计一个有效的算法找到这个下标。
要求算法在最坏的情况下的计算时间为O(logn)。
3、设计一个有效算法,对于一个给定的数组循环左移i位,要求时间复杂度为O(n),辅助空间为O
(1)。
例如对abcdefghijk循环左移3位得到defghijkabc。
4.在一个序列有n个元素,其中出现次数最多的元素称为众数。
请设计算法寻找众数并分析算法的时间复杂性。
(如果假设众数出现次数>n/2,试找一个时间复杂度为o(n)的算法)
三、实验提示
1、用left,right,mid表示二分搜索算法的搜索区间的左端点,右端点和中间点,当搜索元素x在数组中时,返回找到的位置,当搜索元素x不在数组中时,返回left,right即可。
要注意,当搜索元素比第一个元素小时,小于x的最大元素不存在,当搜索元素比最后一个元素大时,大于x的最小元素不存在。
2、用二分搜索算法即可实现。
证明如下:
3.用三次逆转算法实现。
1)用长度为n的数组保存字符串,将字符串进行划分,前i位看成一个子串,后n-i位看成一个子串。
(分为两个子问题)上例:
两个子串abcdefghijk
2)用逆转算法分别将两子串反序。
上例:
abc→cbadefghijk →kjihgfed
(逆转算法:
头尾交换时,只用到一个辅助空间,时间复杂度=两子串长度之和n)
逆转算法结束后数组变成cbakjihgfed
3)将整个字符串再用逆转算法反序。
cbakjihgfed→defghijkabc
(逆转算法:
头尾交换时,只用到一个辅助空间,时间复杂度=字符串长度n)
注:
逆转算法可以写成通用函数。
总之:
时间复杂度为2n=O(n),辅助空间为O
(1)。
4、可以选用一个时间复杂度O(nlogn)的排序算法(最好用分治算法)将数组排序,然后用O(n)时间来统计每个数的出现次数,从而求出众数。
假设众数出现次数>n/2时,算法如下:
1.数字两两比较,如果两者不同,则两个数都丢掉;否则,丢掉一个数
留下另一个数。
最后留下的数(加上未参与比较的数)中,原来的“众数”仍然是众数。
(恰好进行了n/2次测试)
2.如果存在一个数未参与比较,则用它和留下的数依次比较。
如果一半或一半以上的比较都相同,则这个数就是众数(为什么?
反证法),找到众数,算法结束;否则这个数一定不是众数,丢掉它。
(最多进行了n/2次测试)
3.对留下的数中,自然“众数”占多数(为什么?
)。
重复步骤1至2
直到只剩下一个数,这个数就是众数,算法结束。
(可以用队列实现,留下的数入队)
四、实验步骤
1.理解算法思想和问题要求;
2.编程实现题目要求;
3.上机输入和调试自己所编的程序;
4.验证分析实验结果;
5.整理出实验报告。
实验二贪心算法
一、实验目的与要求
1、熟悉多机调度,汽车加油问题的算法;
2、掌握贪心算法思想及步骤;
二、实验题
1、要求给出一种作业调度方案,使得所给的n个作业,尽可能短的时间内由m台机器加工处理完成。
假设作业的编号为0~n-1,加工时间为t0~tn-1,机器编号为0~m-1,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。
作业不能拆分成更小的子作业。
有如下输出格式要求。
----------------------------------------
作业个数:
7↙
机器台数:
3↙
作业0所需要的时间:
2↙
作业1所需要的时间14↙
作业2所需要的时间4↙
作业3所需要的时间16↙
作业4所需要的时间6↙
作业5所需要的时间5↙
作业6所需要的时间3↙
机器0加工总时间16=16
机器1加工总时间17=14+3
机器2加工总时间17=6+5+4+2
机器0上的加工作业编号3
机器1上的加工作业编号16
机器2上的加工作业编号4520
----------------------------------------
2、一辆汽车加满油后可以行驶N千米。
旅途中有若干个加油站。
若要使沿途的加油次数最少,设计一个有效的算法,指出应在哪些加油站停靠加油。
并证明你的算法能产生一个最优解。
3、找零钱问题。
设有m种钱币,编号1~m,面值分别为c1,c2,c3,……cm,枚数分别为n1,n2,n3,……nm,试用上述钱币来找出金额为t的零钱,使得所找零钱的枚数最小。
如果没法找零钱,请打印“无解”。
例如:
面值为1角、2角、5角、1元、2元、5元、10元、20元、50元,100元各有1、1、1、1、1、4、3、1、2、5枚,分别零钱432.5元和345元。
结果如下。
请输入钱币种数:
10↙
请输入面额:
0.1↙枚数:
1↙
请输入面额:
0.2↙枚数:
1↙
请输入面额:
0.5↙枚数:
1↙
请输入面额:
1↙枚数:
1↙
请输入面额:
2↙枚数:
1↙
请输入面额:
5↙枚数:
4↙
请输入面额:
10↙枚数:
3↙
请输入面额:
20↙枚数:
1↙
请输入面额:
50↙枚数:
2↙
请输入面额:
100↙枚数:
5↙
请输入要找的零钱数:
432.5↙
结果如下:
面额0.1的枚数0枚
面额0.2的枚数0枚
面额0.5的枚数1枚
面额1的枚数0枚
面额2的枚数1枚
面额5的枚数0枚
面额10的枚数1枚
面额20的枚数1枚
面额50的枚数0枚
面额100的枚数4枚
总枚数:
8枚
还想要找其它零钱吗?
(Y/N)Y↙
请输入要找的零钱数:
209↙
结果如下:
无解
还想要找其它零钱吗?
(Y/N)N↙
三、实验提示
1、采用处理时间最长的作业优先加工的贪心策略。
1)把作业按加工时间从大到小排序。
(对作业编号按加工时间排序,得到一个加工顺序)
2)每台机器上先按加工顺序分配一个作业,以后一旦有机器空闲,就按加工顺序分配一个作业到空闲机器上,直到所有作业加工完成。
2、把两加油站的距离放在数组中,a[1..n]表示从起始位置开始跑,经过n个加油站,a[k]表示第k-1个加油站到第k个加油站的距离。
贪心策略:
汽车在运行的过程中当前油量如果能跑到下一个站则不加油,否则要加满油。
3、贪心策略:
先找最大面值的,要是不够再找面值小一点的,直到找完为止。
1)对钱币进行编号,对编号按面值从大到小排序,得到一个找钱顺序
2)按找钱顺序进行,如果(剩余零钱/面值)小于等于该面值的最大枚数,则要找的枚数为(剩余零钱/面值),否则要找的枚数为该面值的最大枚数。
3)重新计算剩余零钱,再用面值小一点的钱币进行2)。
4)所有钱币试过后,如果还有零钱没有找,则没法找零钱(无解)。
四、实验步骤
1.理解算法思想和问题要求;
2.编程实现题目要求;
3.上机输入和调试自己所编的程序;
4.验证分析实验结果;
5.整理出实验报告。
实验三动态规划算法
一、实验目的与要求
1、熟悉最长公共子序列问题的算法;
2、熟悉最大子段和,找零钱、最大k乘积问题的算法;
3、掌握动态规划算法思想及步骤;
二、实验题
1、若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有:
zj=xij。
例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。
给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。
给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。
2、最大子段和问题。
若给定n个整数组成的序列a1,a2,a3,……an,求该序列sum=ai+ai+1+……+aj,的最大值。
如果sum的最大值<0,最大子段和规定为0。
(要求用穷举法、分治法和动态规划三种方法,并写出时间复杂度)
3、找零钱问题。
(用动态规划算法解)
设有m种钱币,编号1~m,面值分别为c1,c2,a3,……cm,枚数分别为n1,n2,n3,……nm,试用上述钱币来找出金额为t的零钱,使得所找零钱的枚数最小。
如果没法找零钱,请打印“无解”。
4.最大k乘积问题。
设I是一个n位十进制整数。
如果将I划分为k段,则可得到k个整数。
这k个整数的乘积称为I的一个k乘积。
对于给定的I、n和k,试设计一个算法,编程计算I的最大k乘积。
例如,十进制整数3456的最大3乘积为1020。
十进制整数3456划分成3段有以下3种情形:
3×4×56=672
3×45×6=810
34×5×6=1020
三、实验提示
1、最长公共子序列(见课本)
2、最大子段和问题
1)穷举算法
令max=0maxi=0maxj=0
对i=1ton
对j=1ton
计算ai到aj的和 sum
如果sum>max则max=summaxi=imaxj=j
时间复杂度O(n3)
2)改进的穷举法
设ai到aj-1的和为sum则ai到aj的和sum=sum+aj
请改进穷举算法,求ai到aj的和时,充分利用已有结果:
ai到aj-1的和。
时间复杂度可以降为O(n2)
3)分治法
如果将所给的序列a[1:
n]分为长度相等的两段a[1:
n/2]和a[n/2+1:
n],分别求出这两段的最大子段和,则a[1:
n]的最大子段和有三种情形:
A.a[1:
n]的最大子段和与a[1:
n/2]的最大子段和相同;(s1,maxi1,maxj1)
B.a[1:
n]的最大子段和与a[n/2+1:
n]的最大子段和相同;(s2,maxi2,maxj2)
C.a[1:
n]的最大子段和为下面的形式。
子段横跨两段a[i]+…+a[n/2]+a[n/2+1]+…+a[j]
对于A、B这两种情形可递归求得。
对于情形C,计算如下:
sa=max{a[i]+…+a[n/2]|1<=i<=n/2},并记下sa取最大值时下标maxi3
sb=max{a[n/2+1]+…+a[j]|n/2+1<=j<=n},并记下sb取最大值时下标maxj3
则s3=sa+sb即为C的最大值。
A、B、C三个子段的最大者即为原问题的解。
时间复杂度O(nlogn),请证明。
3)动态规划
令bj=max{a[i]+…+a[j]|1<=i<=j},即以a[j]结尾的最大子段和。
由定义易知,当bj-1>0时bj=bj-1+aj,否则bj=aj。
由此可得计算bj的递归式:
b0=0
bj=max{bj-1+aj,aj},1≤j≤n。
b0、b1、……、bn的最大者为原问题的解。
请说明最优子结构性质,重叠子问题,并说明时间复杂度为O(n)。
3、找零钱问题
设原问题为:
零钱为t,用面值分别为c1,c2,a3,……cm,枚数分别为n1,n2,n3,……nm的钱币找零。
原问题可分为两种情形:
1)用面额为cm的钱币找零。
当t>=cm并且nm>0时,先用面额为cm的钱币找1枚。
剩下零钱为t-cm,再递归地用面值分别为c1,c2,a3,……cm,枚数分别为n1,n2,n3,……nm-1的钱币找零钱。
(子问题1)
2)不用面额为cm的钱币找零。
剩下零钱为t,递归地用面值分别为c1,a2,……cm-1,枚数分别为n1,n2,……nm-1的钱币找零钱。
(子问题2)
3)1)和2)的总钱币数较小者,为原问题的解。
可见原问题分解子问题1和子问题2,并且和原问题的类型相同。
假设P(i,t)为用面值为c1,c2,a3,……ci,枚数分别为n1,n2,n3,……ni的钱币找零钱t所得的最少枚数。
则得递归式:
(∞表示无解)
公式中用到%(求余),对于实验二的例子,把面值和零钱扩大10倍,转化整数(角币)处理。
对于下面例子,试用贪心算法和动态规划求解。
贪心算法能得到最优解吗?
为什么?
面值为1元、6元、15元,有3、3、3枚,零钱18元。
4、最大k乘积问题
设f(i,j)表示i位整数划分成j段后的最大j乘积(j<=i)。
如果前面j-1段有m位组成,则第j段有i-m位组成(从第m+1位到第i位)。
显然f(i,j)具有最优子结构性质。
即前面j-1段组成的m位数的最大j-1乘积为f(m,j-1),则计算f(i,j)的递归式如下:
f(i,1)=I的前i位数其中i>=1
f(i,j)=max{f(m,j-1)*value(j,i,m)|j-1<=m=j,j>1
其中,value(j,i,m)表示第j段组成的i-m位数(从第m+1位到第i位)
求出f(n,k)即可。
如:
求十进制整数4356798分成4段的最大4乘积分为下列情况:
(1)递归地求435679分为3段的最大3乘积,然后乘以8
(2)递归地求43567分为3段的最大3乘积,然后乘以98
(3)递归地求4356分为3段的最大3乘积,然后乘以798
(4)递归地求435分为3段的最大3乘积,然后乘以6798
(5)递归地求43分为3段的最大3乘积,然后乘以56798
→(5)以后这种情况不存在,43无法分为3段。
(1)
(2)(3)(4)的最大值即为原问题的解
四、实验步骤
1.理解算法思想和问题要求;
2.编程实现题目要求;
3.上机输入和调试自己所编的程序;
4.验证分析实验结果;
5.整理出实验报告。
实验四 回溯算法
一、实验目的与要求
1、掌握符号三角形问题的算法;
2、掌握0—1背包问题的回溯算法;
3、理解并掌握回溯算法;
二、实验题图
1、下图是由14个“+”和14个“-”组成的符号三角形。
2个同号下面都是“+”,2个异号下面都是“-”。
+ + - + - + +
+ - - - - +
- + + + -
- + + -
- + -
- -
+
在一般情况下,符号三角形的第一行有n个符号。
符号三角形问题要求对于给定的n,计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。
2、0-1背包问题:
给定n种物品和一背包。
物品i的重量是wi,其价值为vi,背包的容量为C。
问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
三、实验提示
1、符号三角形:
解向量:
用n元组x[1:
n]表示符号三角形的第一行。
可行性约束函数:
当前符号三角形所包含的“+”个数与“-”个数均不超过half=n*(n+1)/4
无解的判断:
n*(n+1)/2为奇数(主程序中判断)
//二维数组p存放符号“+”或“-”
//count计算减号个数
//t第一行第t个符号
//p[j][t]=1第j行的左边数起,第t个符号为”-“
//p[j][t]=0第j行的左边数起,第t个符号为”+“
if((count>half)||(t*(t-1)/2-count>half))return;//无解判断,减号或加号个数超过一半?
if(t>n)………//第一行试探完毕,输出一个答案
for(inti=0;i<2;i++){//对加号和减号试探
p[1][t]=i;//第一行第t个符号设为加号或减号
count+=i;//减号个数累加
for(intj=2;j<=t;j++){//从第2行到第t行
p[j][t-j+1]=p[j-1][t-j+1]^p[j-1][t-j+2];//从第j-1行决定第j行符号(^异或运算)
count+=p[j][t-j+1];//减号个数累加
}
Backtrack(t+1);//递归试探第一行第t+1个符号
for(intj=2;j<=t;j++)
count-=p[j][t-j+1];//减号个数恢复原来的值,进行回溯
count-=i;//减号个数恢复原来的值,进行回溯
}
2、0-1背包问题:
0-1背包问题是子集选取问题。
0-1背包问题的解空间可用子集树表示。
在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入左子树。
当右子树中有可能包含最优解时,才进入右子树搜索,否则将右子树剪去。
设cp是当前价值;bestp是当前最优价值。
计算右子树中解的上界的更好方法是将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包。
由此得到右子树中解的上界b。
当cp+b<=bestp时,可剪去右子树(参照上界函数)。
为了便于计算上界,首先,要对输入数据进行预处理,将各物品的编号依其单位重量价值从大到小进行排列,得到一个放入背包的顺序,此后只要按顺序考察各物品是否要放入背包即可。
1将数组x[1:
n]初始化为0;cw=cp=0;
最优解bestx[1:
n]初始化为0bestp=bestw=0;
child[1:
n]初始化为0//child[k]=0左子树未处理
//=1左子树已处理右子树未处理
//=2左右子树已处理
2k=1;
3while(k>=1)//考察第k件物品
3.1如果k>n得到一个可行解,如果比当前最优解好,替换当前最优解,转3.4
3.2若child[k]=0//处理左子树,物品放入背包
若不超重令x[k]=1,child[k]=1
计算背包中物品重量cw=cw+w[k]和价值cp=cp+p[k];
k=k+1转3
3.3若child[k]=1//处理右子树,物品不放入背包
令x[k]=0,child[k]=2计算上界函数cb,若cb<=bestp,转步骤3.4;
k=k+1转3
3.4k=k-1cw=cw-w[k]*x[k],cp=cp-p[k]*x[k];转3
4.打印最优解。
四、实验步骤
1.理解算法思想和问题要求;
2.编程实现题目要求;
3.上机输入和调试自己所编的程序;
4.验证分析实验结果;
5.整理出实验报告。
实验五分支限界法
一、实验目的与要求
1、掌握0-1背包问题的优先队列式分支限界算法;
2、区分分支限界算法与回溯算法的区别,加深对分支限界法的理解。
二、实验题:
0-1背包问题:
给定n种物品和一背包。
物品i的重量是wi,其价值为vi,背包的容量为C。
问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
三、实验提示
首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。
在优先队列分支限界法中,节点的优先级由已装背包的物品价值加上剩下的物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包所得的价值。
(参照书上的上界函数)
每次扩展时从活结点优先队列中找一个优先级最大的结点进行扩展。
算法首先检查当前扩展结点的左儿子结点的可行性。
如果该左儿子结点是可行结点(背包不超重),则将它加入到子集树和活结点优先队列中。
当前扩展结点的右儿子结点是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。
当扩展到叶节点时为问题的最优值。
//结点定义
StructNode
{
intuprofit;//价值上界//优先级
intprofit;//结点相应的价值(背包中物品的价值之和)
intweight;//结点相应的重量(背包中物品的重量之和)
intlevel;//活结点在子集树中所处的层次(第几件物品)
structNode*parent;//指向上层结点指针(指向前一件物品)
intlchild;//是否上层结点的左孩子(1:
左孩子0:
右孩子)
};
//生成活结点
Node*CreateLiveNode(intup,intcp,intcw,intlchild,intlevel,structNode*parent)
{
Node*b=newNode;
b->parent=parent;
b->lchild=lchild;
b->uprofit=up;
b->profit=cp;
b->weight=cw;
b->level=level;
returnb;
}
//向队列H中增加活结点p
H->EnQueue(p);//入队注意队列中元素为指针
//队列的定义省略,包括InitQueue,EnQueue,DeleteMaxNode-删除队列中优先级最大的结点(自己写)
1令down=0;将队列初始化为空;
2生成一个结点,表示背包中无物品。
放入队列中。
(uprofit=0,level=0,parent=null…,详见队列的结点定义)
3.从队列中取出优先级uprofit最大的结点放入p,
3.1如果是p的层次最大(level=n),则p是最优解,打印结果,结束。
3.2对结点p生成左孩子和右孩子结点。
如果物品放入背包不超重,则同时生成左右孩子,否则只生成右孩子。
(左