算法设计与分析习题Word文档格式.docx
《算法设计与分析习题Word文档格式.docx》由会员分享,可在线阅读,更多相关《算法设计与分析习题Word文档格式.docx(31页珍藏版)》请在冰豆网上搜索。
递归法:
汉诺塔问题?
兔子序列(上楼梯问题)?
整数划分问题?
蛮力法:
百鸡百钱问题?
倒推法:
穿越沙漠问题?
算法如下:
(1)递归法
●汉诺塔问题
voidhanoi(intn,inta,intb,intc)
{if(n>
0)
{
hanoi(n-1,a,c,b);
move(a,b);
hanoi(n-1,c,b,a);
}}
●
兔子序列(fibonaci数列)
递归实现:
IntF(intn)
{
if(n<
=2)return1;
else
returnF(n-1)+F(n-2);
}
上楼梯问题
IntF(intn)
if(n=1)return1
if(n=2)return2;
●整数划分问题
问题描述:
将正整数n表示成一系列正整数之和,n=n1+n1+n3+…
将最大加数不大于m的划分个数,记作q(n,m)。
正整数n的划分数p(n)=q(n,n)。
可以建立q(n,m)的如下递归关系:
递归算法:
Intq(intn,intm){
if(n<
1||m<
1)return0;
If((n=1)||(m=1))return1;
If(n<
m)returnq(n,n);
If(n=m)returnq(n,m-1)+1;
else
returnq(n,m-1)+q(n-m,m);
(2)蛮力法:
百鸡百钱问题
算法设计1:
设x,y,z分别为公鸡、母鸡、小鸡的数量。
约束条件:
x+y+z=100且5*x+3*y+z/3=100
main()
{intx,y,z;
for(x=1;
x<
=20;
x=x+1)
for(y=1;
y<
=34;
y=y+1)
for(z=1;
z<
=100;
z=z+1)
if(100=x+y+zand100=5*x+3*y+z/3)
{ print(thecocknumberis"
x);
print(thehennumberis"
y);
print(thechicknumberis"
z);
算法分析:
以上算法需要枚举尝试20*34*100=68000次。
算法的效率显然太低
算法设计2:
在公鸡(x)、母鸡(y)的数量确定后,小鸡
的数量
z就固定为100-x-y,无需再进行枚举了。
此时约束条件只有一个:
5*x+3*y+z/3=100
main()
{
intx,y,z;
x=x+1)
for(y=1;
=33;
y=y+1)
z=100-x-y;
if(zmod3=0and
5*x+3*y+z/3=100)
{print(thecocknumberis"
}
以上算法只需要枚举尝试20*33=660次。
实现时约束条件又限定Z能被3整除时,才会判断“5*x+3*y+z/3=100”。
这样省去了z不整除3时的算术计算和条件判断,进一步提高了算法的效率。
(3)倒推法:
穿越沙漠问题
desert()
{intdis,k,oil,k;
//dis表示距终点的距离,k表示贮油点从后到前的序号
dis=500;
k=1;
oil=500;
//初始化
while(dis<
1000)
print(“storepoint”,k,”distance”,1000-dis,”oilquantity”,oil)//1000-dis则表示距起点的距离,
k=k+1;
//k表示储油点从后到前的序号
dis=dis+500/(2*k-1);
oil=500*k;
}
print(“storepoint”,k,”distance”,dis,”oilquantity”,oil);
第二章分治算法
1、分治算法基本思想是什么?
适合用分治算法解决的问题,一般具有几个特征?
分治算法基本步骤是什么?
1)基本思想:
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
2)特征:
Ø
该问题的规模缩小到一定的程度就可以容易解决;
该问题可以分解为若干个规模较小的相同子问题,即该问题具有最优子结构性质;
该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
4)利用该问题分解出子问题解可以合并为该问题解;
3)基本步骤:
分解、求小问题解、合并
2、改写二分查找算法:
设a[1…n]是一个已经排好序的数组,改写二分查找算法:
✓当搜索元素x不在数组中时,返回小于x的最大元素位置i,和大于x的最小元素位置j;
(即返回x的左、右2个元素)
✓当搜索元素x在数组中时,i和j相同,均为x在数组中的位置。
并计算其时间复杂度?
3、设计一个合并排序的算法?
(分治法解) 并计算其时间复杂度?
(要求写出递推公式,及其求解过程)
voidMergeSort(intA[],intlow,inthigh)
{intmiddle;
if(low<
high)
middle=(low+high)/2;
//取中点
MergeSort(A,low,middle);
MergeSort(A,middle+1,high);
Merge(A,low,middle,high);
//合并算法
voidMerge(intA[],intlow,intmiddle,inthigh)//合并过程描述:
inti,j,k;
int*B=newint[high-low+1];
i=low;
j=middle+1;
k=low;
while(i<
=middle&
&
j<
=high){ //两个子序列非空
if(A[i]<
=A[j]) B[k++]=A[i++];
else B[k++]=A[j++];
while(i<
=middle) B[k++]=A[i++];
//子序列A[low,middle]非空,将A复制到B
while(j<
=high) B[k++]=A[j++];
/子序列A[middle+1,high]非空,将A复制到B
for(i=low;
i<
=high;
i++) A[i++]=B[i++];
//将合并后的序列复制回A
•合并排序算法运行时间T(n)的递归形式为:
◆分析该算法时间复杂度:
令T(n)为元素个数为n时所需比较次数(时间):
当n=1时, 时间复杂度记为O
(1)。
当n>
1时,T(n)=2T(n/2)+O(n)
=2(2T(n/22)+O(n/2))+O(n)
=22T(n/22)+2O(n)
=23T(n/23)+3O(n)
=……
=2xT(n/2x)+x*O(n)
分解到最后只有2个元素可以求解,n/2x=1,x=logn;
故T(n)=n*T
(1)+n*logn,故时间复杂度记为:
O(n*logn)
4、金块问题(求最大最小元问题)
老板有一袋金块(共n块),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块。
假设有一台比较重量的仪器,我们希望用最少的比较次数找出最重的金块。
要求:
1)设计一算法求解该问题?
(分治法解)
2)计算其时间复杂度?
递归求取最大和最小元素
maxmin(inti,intj,float&
fmax,float&
fmin)
{intmid;
floatlmax,lmin,rmax,rmin;
if(i=j){fmax=a[i];
fmin=a[i];
}//只有1个元素
elseif(i=j-1)//只有2个元素
if(a[i]<
a[j]){fmax=a[j];
fmin=a[i];
else{fmax=a[i];
fmin=a[j];
else//多于2个元素
{mid=(i+j)/2;
maxmin(i,mid,lmax,lmin);
//递归调用算法求最大最小
maxmin(mid+1,j,rmax,rmin);
//递归调用算法求最大最小
if(lmax>
rmax)fmax=lmax;
//合并取大
elsefmax=rmax;
if(lmin>
rmin)fmin=rmin;
//合并取小
elsefmin=lmin;
当n=2时,查找查找最大最小元只需要1次比较,T
(2)=1;
时间复杂度记为O
(1)。
2时,T(n)=2T(n/2)+2T
(2)
=4T(n/4)+4T
(2)+2T
(2)
=8T(n/8)+8+4+2
=2xT(n/2x)+2x+2x-1+…+8+4+2
分解到最后只有2个元素可以求解,n/2x=2,
T(n)=2x*1+2x+2x-1…+22+21
=2x*1+(2-2x*2)/(1-2)
=2x+2x+1-2
=3n/2-2
故时间复杂度记为:
O(n)
5、用分治思想设计一个有效的算法,可以进行两个n位大整数的乘法运算?
并计算其时间复杂度?
答:
intmult(intx,inty,intn)//x,y为两个n位整数
{s=sign(x)*sign(y);
//s为x*y的符号
x=abs(x);
y=abs(y);
intmul;
if(n=1){mul=s*x*y;
returnmul;
else//计算XY=ac2n+((a-b)(d-c)+ac+bd)2n/2+bd
{inta=x左边n/2位;
//移位操作,把X分为2块
intb=x右边n/2位;
intc=y左边n/2位;
//移位操作,把Y分为2块
intd=y右边n/2位;
intm1=mult(a,c,n/2);
//a,c还不够小继续分为2块,直到最后1×
1位
intm2=mult(a-b,d-c,n/2);
intm3=mult(b,d,n/2);
mul=s*(m1*2n+(m1+m2+m3)*2n/2+m3);
returnmul;
}}
6、设计一棋盘覆盖问题算法(分治法)?
在一个2k×
2k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
(该算法中可能用到的变量:
tr:
棋盘中左上角方格所在行;
tc:
棋盘中左上角方格所在列。
dr:
残缺方块所在行;
dl:
残缺方块所在列。
size:
棋盘的行数或列数;
用二维数组board[][],模拟棋盘。
)
voidchessBoard(inttr,inttc,intdr,intdc,intsize)
if(size==1)return;
//size:
棋盘行数
intt=tile++,//L型骨牌号
s=size/2;
//分割棋盘
//覆盖左上角子棋盘
if(dr<
tr+s&
dc<
tc+s)//特殊方格在此棋盘中
chessBoard(tr,tc,dr,dc,s);
else{//此棋盘中无特殊方格
board[tr+s-1][tc+s-1]=t;
//用t号L型骨牌覆盖右下角
chessBoard(tr,tc,tr+s-1,tc+s-1,s);
}//覆盖其余方格
//覆盖右上角子棋盘
dc>
=tc+s)//特殊方格在此棋盘中
chessBoard(tr,tc+s,dr,dc,s);
board[tr+s-1][tc+s]=t;
//用t号L型骨牌覆盖左下角
chessBoard(tr,tc+s,tr+s-1,tc+s,s);
//覆盖左下角子棋盘
if(dr>
=tr+s&
chessBoard(tr+s,tc,dr,dc,s);
else{
board[tr+s][tc+s-1]=t;
//用t号L型骨牌覆盖右上角
chessBoard(tr+s,tc,tr+s,tc+s-1,s);
//覆盖右下角子棋盘
=tc+s)//特殊方格在此棋盘中
chessBoard(tr+s,tc+s,dr,dc,s);
board[tr+s][tc+s]=t;
//用t号L型骨牌覆盖左上角
chessBoard(tr+s,tc+s,tr+s,tc+s,s);
第三章动态规划算法
1、动态规划算法基本思想?
动态规划算法与分治算法异同点?
适合用动态规划算法求解问题的基本要素?
动态规划算法的基本步骤?
1)基本思想:
将待求解问题分解成若干个子问题;
由于子问题有重叠,动态规划算法能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,就可以避免大量重复计算.
2)相同:
都是将原问题分解成小问题,通过小问题求解得到原问题解。
不同:
✓用分治法求解时,分解的子问题是互相独立的,且与原问题类型一致。
分治算法实现一般用递归;
✓动态规划方法经分解得到的子问题往往不是互相独立的;
动态规划算法实现一般用循环;
3)基本要素:
具有最优子结构;
子问题具有重叠性
4)步骤:
1)分析最优解的性质,并刻划其结构特征。
2)递推地定义最优值。
3)以自底向上的方式计算出最优值.
4)根据计算最优值时得到的信息,构造问题的最优解.
2、序列X={X1,X2,…Xm}和Y={Y1,Y2…Yn}的最长公共子序列为Z={Z1,Z2,…Zk}
用动态规划的方法求序列X和Y的最长公共子序列长度?
(要求按照动态规划写出动态规划求解问题的步骤分析①最优子结构②写出递归方程③算法描述)
注:
C[m][n]记录序列X与Y的最长公共子序列的长度
①最优子结构
设序列X={x1,x2,…xm}与
序列Y={y1,y2,…yn}的一个
最长公共子序列Z={z1,z2,…zk}
Ⅰ、若xm=yn,则zk=xm=yn,且{z1,z2,…zk-1}是序列Xm-1与
序列Yn-1的最长公共自序列;
Ⅱ、若xm≠yn,且xm≠zk,则Z是Xm-1与Y的最长公共子序列;
Ⅲ、若xm≠yn,且yn≠zk,则Z是X与Yn-1的最长公共子序列;
由此可见,2个序列的最长公共子序列包含了这2个序列的前缀(去掉一个元素)的最长公共子序列。
即,原问题最优解,包含子问题最优解;
因此,最长公共子序列问题具有最优子结构性质。
②写出递归方程
③循环实现,计算最优值C[i][j],算法描述
IntlcsLength(x[],y[],b[][])
{intm=x.length-1;
n=y.length-1;
for(inti=1;
i<
m;
i++)C[i][0]=0;
//y序列空
n;
i++)C[0][i]=0;
//x序列空
for(inti=1;
i<
=m;
i++)//x序列长为m
for(intj=1;
j<
=n;
j++) //y序列长为n
if(x[i]==y[j])
{C[i][j]=C[i-1][j-1]+1;
b[i][j]=1;
elseif(c[i-1][j]>
=c[i][j-1])
{C[i][j]=C[i-1][j];
b[i][j]=2;
else
{C[i][j]=C[i][j-1];
b[i][j]=3;
returnC[m][n];
◆时间复杂度分析:
该算法时间复杂度:
O(m*n)
④构造最长公共子序列,算法描述:
voidLCS(charX[i],Y[j],intb[][])
if(i==0||j==0)return;
if(b[i][j]==1)
{LCS(X[i-1],Y[j-1],b);
system.out.print(x[i]);
elseif(b[i][j]==2)
LCS(X[i-1],Y[j],b);
elseif(b[i][j]==3)
LCS(X[i],Y[j-1],b);
此算法每一次递归调用使得i或j减1,因此该算法时间复杂度为O(m+n)
3、长江游艇俱乐部在长江上设置了n个游艇出租站1,2…n.
游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。
游艇出租站i到游艇出租站j之间的租金为r(i,j),其中1<
=i<
=n;
试设计一个算法,计算出游艇从出租站1到出租站n所需最少租金?
(见习题集第三章算法设计与计算题T2)
4、掌握动态规划方法求解0-1背包问题?
①分析问题的最优解结构
设(y1,y2,…yn)所给0-1背包容量为M的解;
则,(y2,…yn)相应子问题背包容量为M-w1的解;
(即原问题最优解,包含了子问题最优解)
②递归定义最优值
③计算最优值m(i,j)
voidknapsack(intv[],intw[],intM,intm[][])
{intn=v.length;
if(M<
w[n]) //i=n时,只有1个物品
m[n][M]=0;
elseif(M>
=w[n])
{m[n][M]=v[n];
M=M-w[n];
for(inti=n-1;
i>
=1;
i--)//i<
n时,xi…xn多个物品
{if(M<
w[i])
m[i][M]=m[i+1][M];
elseif(M>
{m[i][M]=math.max(m[i+1][M],m[i+1][M-w[i]+v[i]);
M=M-w[i];
◆该算法时间复杂度:
O(c*n) c常数
④构造最优解
voidtrackack(intm[][],intw[],intM,intx[])
{//x[i]标记i是否放入背包
intn=w.length;
for(inti=1;
i++)//判断前n-1个物体是否放入背包
{if(m[i][M]=m[i+1][M])x[i]=0;
{x[i]=1;
x[n]=(m[n][M]>
0)?
1:
0;
//判断第n个物体是否放入背包
O(n)
第4章贪心算法
1、贪心算法基本思想?
从问题的初始解出发逐步逼近给定的目标,每一步都做出当前看来是最优的选择(贪心选择),最终得到整个问题的最优解
2、贪心算法的基本要素?
贪心选择性;
最优子结构
3、贪心算法与动态规划算法的异同?
1)相同点:
对于要求解的问题都具有最优子结构;
2)不同点:
算法的基本思想不同;
求解问题的类型不同;
例:
普通背包问题 贪心算法求解
0-1背包问题 动态规划算法求解
4、设计普通背包装载问题的贪心算法?
并分析其时间复杂度