《算法分析与设计》实验指导书Word下载.docx
《《算法分析与设计》实验指导书Word下载.docx》由会员分享,可在线阅读,更多相关《《算法分析与设计》实验指导书Word下载.docx(18页珍藏版)》请在冰豆网上搜索。
2、0-1背包问题16
3、装载问题17
18
实验一递归与分治
4学时
一个规模为n的复杂问题的求解:
可以划分成若干个规模较小<
n的子问题进行求解,再将子问题的解合并成原问题的解,这便是分治的思想。
若划分成的每一个子问题都与原问题的性质相同,可用相同的求解方法;
当子问题规模划分一定小时,子问题的解已知,则逆求原问题的解,这是递归的思想。
1、汉诺塔(hanoi)问题。
设有A、B、C共3根塔座,在塔座A上堆叠n个金盘,每个盘大小不同,只允许小盘在大盘之上,最底层的盘最大,如下图所示。
现在要求将A上的盘全都移到C上,在移的过程中要遵循以下原则:
每次只能移动一个盘;
圆盘可以插在A、B和C任一个塔座上;
在任何时刻,大盘不能放在小盘的上面。
hanoi问题递归求解思想:
我们把一个规模为n的hanoi问题:
1到n号盘按照移动规则从A上借助B移到C上表示为H(A,B,C,n);
原问题划分成如下三个子问题:
(1)将1到n-1号盘按照移动规则从A上借助C移到B上H(A,C,B,n-1);
(2)将n号盘从A上直接移到C上;
(3)将1到n-1号盘按照移动规则从B上借助A移到C上H(B,A,C,n-1);
经过三个子问题求解,原问题的也即求解完成。
hanoi问题递归求解代码:
voidH(charA,charB,charC,intn)
{
if(n>
0)
{
H(A,C,B,n-1);
printf(“%dfrom%cto%c”,n,A,C);
H(B,A,C,n-1);
}
}
2、二分查找问题
(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)。
boolBinarySearch(inta[],intn,intx,int&
i,int&
j)
intleft=0;
intright=n-1;
while(left<
right)
intmid=(left+right)/2;
if(x==a[mid])
i=j=mid;
returntrue;
if(x>
a[mid])
left=mid+1;
else
right=mid-1;
i=right;
j=left;
returnfalse;
intSearchTag(inta[],intn,intx)
if(x==a[mid])returnmid;
return-1;
}
3、快速排序问题
在快速排序中,记录的比较和交换是从两端向中间进行的,关键字较大的记录一次就能交换到后面单元,关键字较小的记录一次就能交换到前面单元,记录每次移动的距离较大,因而总的比较和移动次数较少。
typedefintType;
voidQuickSort(Typea[],intp,intr)
if(p<
r){
intq=Partition(a,p,r);
QuickSort(a,p,q-1);
//对左半段排序
QuickSort(a,q+1,r);
//对右半段排序
template<
classType>
intPartition(Typea[],intp,intr)
inti=p,j=r+1;
Typex=a[p];
//将<
x的元素交换到左边区域
//将>
x的元素交换到右边区域
while(true){
while(a[++i]<
x);
while(a[--j]>
if(i>
=j)break;
Swap(a[i],a[j]);
a[p]=a[j];
a[j]=x;
returnj;
1.递归的关键问题在哪里?
2.递归与非递归之间程序的转换?
实验二贪心选择算法
理解贪心选择算法的思想
贪心选择算法思想:
(1)贪心选择能得到问题的最优解,要证明我们所做的第一步选择一定包含在一个最优解总,即存在一个最优解的第一步是从我们的贪心选择开始。
(2)在做出第一步贪心选择后剩下的子问题应该是和原问题类似的规模较小的子问题为此我们可以用数学归纳法来证明贪心选择能得到问题的最优解。
1、单源最短路径
在无向图G=(V,E)中,假设每条边E[i]的长度为w[i],找到由顶点V0到其余各点的最短路径。
按路径长度递增次序产生最短路径算法:
把V分成两组:
(1)S:
已求出最短路径的顶点的集合
(2)V-S=T:
尚未确定最短路径的顶点集合
将T中顶点按最短路径递增的次序加入到S中,
保证:
(1)从源点V0到S中各顶点的最短路径长度都不大于
从V0到T中任何顶点的最短路径长度
(2)每个顶点对应一个距离值
S中顶点:
从V0到此顶点的最短路径长度
T中顶点:
从V0到此顶点的只包括S中顶点作中间
顶点的最短路径长度
依据:
可以证明V0到T中顶点Vk的最短路径,或是从V0到Vk的
直接路径的权值;
或是从V0经S中顶点到Vk的路径权值之和
(反证法可证)
单源最短路径代码:
voidDijkstra(AdjMGraphG,intv0,int*dist,int*path)
{intn=G.Vertices.size;
inti,j,k,pre,min;
int*s=(int*)malloc(sizeof(int)*n);
for(i=0;
i<
n;
i++)//初始化
{s[i]=0;
dist[i]=G->
edge[v0][i];
if(i!
=v0&
&
dist[i]<
MaxWeight)path[i]=v0;
elsepath[i]=-1;
}
s[v0]=1;
//标记v0已从集合T中加入到S
for(i=1;
n;
i++)//
{
min=MaxWeight;
for(j=0;
j<
j++)//找最小dist[j]
if(s[j]==0&
dist[j]<
min)
{min=dist[j];
k=j;
if(min==MaxWeight)return;
S[k]=1;
//标记k已从集合T中加入到S
for(j=0;
j++)//修改dist[j]
if(s[j]==0&
dist[j]>
dist[k]+G->
edge[k][j])
{dist[j]=dist[k]+G->
edge[k][j];
path[j]=k;
}
}
2、背包问题
给定n种物品和一个背包。
物品i的重量是Wi,其价值为Vi,背包的容量为C。
应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
voidKnapsack(intn,floatM,floatv[],floatw[],floatx[])
Sort(n,v,w);
inti;
for(i=1;
=n;
i++)x[i]=0;
floatc=M;
i++){
if(w[i]>
c)break;
x[i]=1;
c-=w[i];
if(i<
=n)x[i]=c/w[i];
//最后一个物品运行部分转入
3、多机调度问题
要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
约定,每个作业均可在任何一台机器上加工处理,但未完工前不允许中断处理。
作业不能拆分成更小的子作业。
实验步骤:
1)、把作业按加工所用的时间从大到小排序
2)、如果作业数目比机器的数目少或相等,则直接把作业分配下去
3)、
如果作业数目比机器的数目多,则每台机器上先分配一个作业,如下的作业分配时,是选那个表头上s最小的链表加入新作业。
typedefstructJob
intID;
//作业号
inttime;
//作业所花费的时间
}Job;
typedefstructJobNode//作业链表的节点
JobNode*next;
}JobNode,*pJobNode;
typedefstructHeader
//链表的表头
ints;
pJobNodenext;
}Header,pHeader;
intSelectMin(Header*M,intm)
intk=0;
for(inti=1;
m;
i++)
if(M[i].s<
m[k].s)k=i;
returnk;
1、贪心算法与动态规划思想解题的区别?
1、哈夫曼编码问题的编程实现?
实验三动态规划算法
理解动态规划算法的思想
动态规划算法思想:
把待求解问题分解成若干个子问题,先求解子问题,然后由这些子问题的解得到原问题的解,但动态规划求解过的子问题的结果会被保留下来,不像递归那样每个子问题的求解都要从头开始返回求解。
动态规划求解问题的关键在于获得各个阶段子问题的递推关系式:
(1)分析原问题的最优解性质,刻画其结构特征
(2)递归定义最优值
(3)自底向上(由后向前)的方式计算最优值
(4)根据计算最优值时得到的信息,构造一个最优解。
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的最长公共子序列。
voidLCSLength(char*x,char*y,intm,intn,int**c,int**b)
inti,j;
for(i=1;
i<
=m;
i++)c[i][0]=0;
=n;
i++)c[0][i]=0;
i++)
for(j=1;
j<
j++)
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;
voidLCS(inti,intj,char*x,int**b)
if(i==0||j==0)return;
if(b[i][j]==1)
LCS(i-1,j-1,x,b);
printf("
%c"
x[i]);
elseif(b[i][j]==2)
LCS(i-1,j,x,b);
elseLCS(i,j-1,x,b);
2、最长最大字段和问题
若给定n个整数组成的序列a1,a2,a3,……an,求该序列形如ai+ai+1+……+an的最大值。
源程序:
#include<
iostream.h>
#defineM1001
inta[M];
intMaxSum(intn,int&
besti,int&
bestj)
intsum=0;
intb=0;
intbegin=0;
for(inti=1;
if(b>
0)b+=a[i];
else{b=a[i];
begin=i;
sum)
sum=b;
besti=begin;
bestj=i;
returnsum;
voidmain()
intn;
intbesti,bestj;
cout<
<
"
请输入数组的长度:
endl;
cin>
>
while(true)
besti=0;
bestj=0;
请输入数组的内容,以空格隔开:
i<
a[i];
最大子段和为:
MaxSum(n,besti,bestj)<
最大字段和的序列是从"
besti<
到"
bestj<
break;
}
实验结果
1、深刻理解动态规划与递归求解问题的区别?
2、动态规划思想解题的步骤?
实验四搜索算法
理解搜索算法的思想
6学时
回溯法的基本做法是搜索,或是一种组织得井井有条的、能避免不必要搜索的穷举式搜索法。
这种方法适用于解一些组合数相当大的问题。
回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。
算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解:
如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;
否则,进入该子树,继续按深度优先策略搜索。
回溯法的基本步骤:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
常用剪枝函数:
用约束函数在扩展结点处剪去不满足约束的子树;
用限界函数剪去得不到最优解的子树。
1、n皇后问题
n皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。
在nXn格的棋盘上摆放n个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
intplace(intk,intx[])
inti=1;
while(i<
k)
if((x[i]==x[k])||abs(i-k)==abs(x[i]-x[k]))return0;
i++;
return1;
voidbacktrack(intt,intx[])
if(t>
n)
for(i=1;
printf(“%4d”,x[i]);
printf(“\n”);
x[t]=i;
if(place(t,x)==1)backtrack(t+1,x);
2、0-1背包问题
有n件物品和一个容量为c的背包。
第i件物品的容量是w[i],价值是p[i]。
求解将哪些物品装入背包可使价值总和最大。
#include<
conio.h>
#definec30
#definen3
intbound(inti,intcw,intcp,intw[],intp[])
intcleft=c-cw;
intb=cp;
=n&
w[i]<
=cleft)
cleft-=w[i];
b+=p[i];
if(i<
=n)b+=p[i]/w[i]*cleft;
returnb;
voidback(inti,intcw,intcp,int*bestp,intw[],intp[],intx[])
if(i>
{
*bestp=cp;
return;
if(cw+w[i]<
=c)
cw+=w[i];
cp+=p[i];
x[i]=1;
back(i+1,cw,cp,bestp,w,p,x);
cw-=w[i];
cp-=p[i];
if(bound(i+1,cw,cp,w,p)>
*bestp)
x[i]=0;
back(i+1,cw,cp,bestp,w,p,x);
main()
intcw=0,cp=0,bestp=0,x[5];
intw[]={0,16,15,15},p[]={0,45,25,25};
inti;
clrscr();
back(1,cw,cp,&
bestp,w,p,x);
%d\n"
bestp);
%2d"
getch();
3、装载问题
有一批共n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。
如果有,找出一种装载方案。
voidbacktrack(inti)
{//搜索第i层结点
n)//到达叶结点
更新最优解bestx,bestw;
return;
r-=w[i];
if(cw+w[i]<
=c){//搜索左子树
x[i]=1;
cw+=w[i];
backtrack(i+1);
cw-=w[i];
if(cw+r>
bestw){
x[i]=0;
//搜索右子树
r+=w[i];
1、启发式搜索问题?
2、搜索算法的解空间树的定义?
3、0-1背包问题的动态规划求解?