实验二 动态规划算法李明明.docx
《实验二 动态规划算法李明明.docx》由会员分享,可在线阅读,更多相关《实验二 动态规划算法李明明.docx(21页珍藏版)》请在冰豆网上搜索。
实验二动态规划算法李明明
实验二动态规划算法(2学时)
基本题一:
最长公共子序列问题
一、实验目的与要求
1、熟悉最长公共子序列问题的算法;
2、初步掌握动态规划算法;
二、实验题
若给定序列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的最长公共子序列。
三、实验提示
include"stdlib.h"
#include"string.h"
voidLCSLength(char*x,char*y,intm,intn,int**c,int**b)
{
inti,j;
for(i=1;i<=m;i++)c[i][0]=0;
for(i=1;i<=n;i++)c[0][i]=0;
for(i=1;i<=m;i++)
for(j=1;j<=n;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);
}
四、算法思想
1)动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
2)与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是独立的。
子问题中存在大量的公共子问题,在分治求解过程中被多次重复计算,保存计算结果,为后面的计算直接引用,减少重复计算次数这就是动态规划的基本思想。
3)用动态规划算法求解问题,可依据其递归式以自底向上的方式进行计算。
在计算过程中,保存已解决的子问题的答案。
每个子问题只计算一次,而在后面需要时只要简单查一下,从而避免大量重复计算,最终得到多项式时间算法
五、算法设计与实现
#include"iostream.h"
#include"iomanip.h"
#definemax100
voidLCSLength(intm,intn,char*x,char*y,char*b)
{
inti,j,k;
intc[max][max];
for(i=1;i<=m;i++)
{
c[i][0]=0;
}
for(i=1;i<=n;i++)
{
c[0][i]=0;
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(x[i-1]==y[j-1])
{
c[i][j]=c[i-1][j-1]+1;
k=i*(n+1)+j;
b[k]='\\';
}
elseif(c[i-1][j]>=c[i][j-1])
{
c[i][j]=c[i-1][j];
k=i*(n+1)+j;
b[k]='|';
}
else{
c[i][j]=c[i][j-1];
k=i*(n+1)+j;
b[k]='-';
}
}
}
}
voidLCS(inti,intj,char*x,char*b,intwidth)
{
if(i==0||j==0)
return;
intk=i*(width+1)+j;
if(b[k]=='\\'){
LCS(i-1,j-1,x,b,width);
cout<}
elseif(b[k]=='|')
{
LCS(i-1,j,x,b,width);
}
else
{
LCS(i,j-1,x,b,width);
}
}
voidmain()
{
charx[max]={'a','b','c','b','d','a','b'};
chary[max]={'b','d','c','a','b','a'};
intm=7;
intn=6;
charb[max]={0};
LCSLength(m,n,x,y,b);
LCS(m,n,x,b,n);
cout<}
最长公共子序列问题具有最优子结构性质
设
X = { x1 , ... , xm }
Y = { y1 , ... , yn }
及它们的最长子序列
Z = { z1 , ... , zk }
则
1、若 xm = yn , 则 zk = xm = yn,且Z[k-1] 是 X[m-1] 和 Y[n-1] 的最长公共子序列
2、若 xm !
= yn ,且 zk !
= xm , 则 Z 是 X[m-1] 和 Y 的最长公共子序列
3、若 xm !
= yn , 且 zk !
= yn , 则 Z 是 Y[n-1] 和 X 的最长公共子序列
由性质导出子问题的递归结构
当 i = 0 , j = 0 时 , c[i][j] = 0
当 i , j > 0 ; xi = yi 时 , c[i][j] = c[i-1][j-1] + 1
当 i , j > 0 ; xi !
= yi 时 , c[i][j] = max { c[i][j-1] , c[i-1][j] }
#include
#definemax(a,b)a>b?
a:
b
#defineM100
voiddisplay(int&n,int&C,intw[M],intv[M])
{
inti;
cout<<"请输入物品种数n:
";
cin>>n;
cout<cout<<"请输入背包总容量C:
";
cin>>C;
cout<cout<<"请输入各物品的大小或重量w:
"<w[0]=0;
for(i=1;i<=n;i++)
cin>>w[i];
cout<<"请输入各物品其价值v:
"<v[0]=0;
for(i=1;i<=n;i++)
cin>>v[i];
};
intknapsack(int&n,int&C,intw[M],intv[M],intV[M][M])
{
inti,j;
for(i=0;i<=n;i++)
for(j=0;j<=C;j++)
{
if(i==0||j==0)
V[i][j]=0;
elseif(w[i]>j)
V[i][j]=V[i-1][j];
elseif(w[i]<=j)
V[i][j]=max(V[i-1][j],V[i-1][j-w[i]]+v[i]);
}
returnV[n][C];
};
voidtraceback(intn,intC,intw[M],intx[M],intV[M][M])
{
for(inti=1;i<=n;i++)
{
if(V[i][C]==V[i-1][C])
x[i]=0;
else
{
x[i]=1;
C=C-w[i];
}
}
//x[n]=(V[n][C]>0)?
1:
0;
};
voidmain()
{
inti,j,n,C;
charch;
intw[M],v[M],x[M];
intV[M][M];
while
(1)
{
display(n,C,w,v);
cout<<"运算结果如下:
"<for(i=1;i<=n;i++)
x[i]=0;
knapsack(n,C,w,v,V);
cout<<"";
for(j=0;j<=C;j++)
cout<cout<for(i=0;i<=n;i++)
{
cout<
for(j=0;j<=C;j++)
{
cout<}
cout<cout<}
cout<<"选择的物向量表示为:
";
cout<<"(";
traceback(n,C,w,x,V);
for(i=1;i<=n;i++)
cout<cout<<")"<cout<<"背包最大价值为:
"<cout<cout<<"按Y或y继续操作,否则按任意键"<cin>>ch;
if(ch=='Y'||ch=='y')
continue;
else
break;
}
}
六、实验总结
本次实验通过动态算法解决最长公共子序列问题,对于求两个序列的一个最长公共子序列,LCSlength算法时间复杂性为0(alen*blen),能够得到较满意的结果。
但另一个方面,这种算法在对c[i-l][j]与c[i][j-l]值的比较中忽略了相等的情况,即在两个序列的最长公共子序列不唯一时不可能求出所有的最长公共子序列。
基本题二:
最大字段和问题
一、实验目的与要求
1、熟悉最长最大字段和问题的算法;
2、进一步掌握动态规划算法;
二、实验题
若给定n个整数组成的序列a1,a2,a3,……an,求该序列形如ai+ai+1+……+an的最大值。
三、实验提示
intMaxSum(intn,int*a,int&besti,int&bestj)
{
intsum=0;
for(inti=1;i<=n;i++)
for(intj=i;j<=n;j++)
{
intthissum=0;
for(intK=i;k<=j;k++)thissum+=a[k];
if(thissum>sum)
{
sum=thissum;
besti=i;
bestj=j;
}
}
returnsum;
}
intMaxSum(intn,int*a,int&besti,int&bestj)
{
intsum=0;
for(inti=1;i<=n;i++)
{
intthissum=0;
for(intj=i;j<=n;j++)
{
thissum+=a[j];
if(thissum>sum)
{
sum=thissum;
besti=i;
bestj=j;
}
}
}
returnsum;
}
四、算法思想
这个算法可以通过动态规划分解为两步:
1,计算辅助数组。
2,计算辅助数组的最大值。
辅助数组b[j]用来记录一j为尾的子段和集合中的最大子断和。
例如,假如有一序列:
-2,11,-4,13,-5,-2
则
b
(1)=-2,b
(2)=11,b(3)=7,b(4)=20,b(5)=15,b(6)=13
a
(1)=-2,a
(2)=11,a(3)=7,a(4)=13,a(5)= -5,a(6)=-2
b
(1)<0 b
(2)>0 b(3)>0 b(4)>0 b(5)>0 b(6)>0
---->
{b(j-1)+a(j) 当b(j-1)>=0
b(j)={
{a(j) 当b(j-1)<0
五、算法设计与实现
intmax_sum(intn,int*a,int*besti,int*bestj)
{
int*b=(int*)malloc(n*sizeof(int));
intsum=0;
inti=-1;
inttemp=0;
for(i=0;i<=n-1;i++){
if(temp>0)
temp+=a[i];
else
temp=a[i];
b[i]=temp;
}
sum=b[0];
for(i=1;i<=n-1;i++){
if(sum
sum=b[i];
*bestj=i;
}
}
for(i=*bestj;i>=0;i--){
if(b[i]==a[i]){
*besti=i;
break;
}
}
free(b);
returnsum;
}
#include
#include
intmax_sum(intn,int*a,int*besti,int*bestj)
{
int*b=(int*)malloc(n*sizeof(int));
intsum=0;
inti=-1;
inttemp=0;
for(i=0;i<=n-1;i++){
if(temp>0)
temp+=a[i];
else
temp=a[i];
b[i]=temp;
}
sum=b[0];
for(i=1;i<=n-1;i++){
if(sum
sum=b[i];
*bestj=i;
}
}
for(i=*bestj;i>=0;i--){
if(b[i]==a[i]){
*besti=i;
break;
}
}
free(b);
returnsum;
}
intmain(void)
{
inta[]={-2,1,-4,13,-5,-2,-10,20,100};
intlength=sizeof(a)/sizeof(int);
intsum=-1;
intbesti=-1;
intbestj=-1;
sum=max_sum(length,a,&besti,&bestj);
printf("besti=%d,bestj=%d,max_sum=%d\n",besti,bestj,sum);
return0;
}
#include
//这个穷举法将分段和计算合在一起,将计算段和复合在分段中,故只有n^2的复杂度。
intmax_sum(intn,int*a,int*besti,int*bestj)
{
intsum=0;
intthissum=0;
inti=0,j=0,k=0;
/*--------------------------------------------------------------*/
for(i=0;i<=n-1;i++){//穷举出所有的段。
thissum=0;
for(j=i;j<=n-1;j++){//段数为:
n+(n-1)+(n-2)+...+1=(n+1)*n/2
/*--------------------------------------------------------------*/
thissum+=a[j];//在分段的时候顺便附带上计算段和。
if(thissum>sum){
sum=thissum;
*besti=i;//记录下起点位置
*bestj=j;//记录下结束位置
}
}
}
returnsum;
}
intmain(void)
{
inta[]={-2,11,-4,13,-5,-2,32,2,-100,2};
intlength=sizeof(a)/sizeof(int);
intbesti=-1;
intbestj=-1;
intsum=-1;
sum=max_sum(length,a,&besti,&bestj);
printf("besti=%d,bestj=%d,max_sum=%d\n",besti,bestj,sum);
return0;
}
六、实验总结
提高题一:
用动态规划法求解0/1背包问题
一、实验要求与目的
1、掌握动态规划算法求解问题的一般特征和步骤。
2、使用动态规划法编程,求解0/1背包问题。
二、实验内容
1、问题描述:
给定n种物品和一个背包,物品i的重量是Wi,其价值为Vi,问如何选择装入背包的物品,使得装入背包的物品的总价值最大?
2、算法描述。
3、程序实现;给出实例测试结果。
三、算法分析
问题分析:
令V(i,j)表示在前i(1<=i<=n)个物品中能够装入容量为就j(1<=j<=C)的背包中的物品的最大价值,则可以得到如下的动态规划函数:
(1) V(i,0)=V(0,j)=0
(2) V(i,j)=V(i-1,j) j V(i,j)=max{V(i-1,j),V(i-1,j-wi)+vi)}j>wi
(1)式表明:
如果第i个物品的重量大于背包的容量,则装人前i个物品得到的最大价值和装入前i-1个物品得到的最大价是相同的,即物品i不能装入背包;第
(2)个式子表明:
如果第i个物品的重量小于背包的容量,则会有一下两种情况:
(a)如果把第i个物品装入背包,则背包物品的价值等于第i-1个物品装入容量位j-wi 的背包中的价值加上第i个物品的价值vi;(b)如果第i个物品没有装入背包,则背包中物品价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。
显然,取二者中价值最大的作为把前i个物品装入容量为j的背包中的最优解
四、算法设计与实现
#include"stdio.h"
intV[200][200];//前i个物品装入容量为j的背包中获得的最大价值
intmax(inta,intb)
{
if(a>=b)
returna;
elsereturnb;
}
intKnapSack(intn,intw[],intv[],intx[],intC)
{
inti,j;
for(i=0;i<=n;i++)
V[i][0]=0;
for(j=0;j<=C;j++)
V[0][j]=0;
for(i=0;i<=n-1;i++)
for(j=0;j<=C;j++){
if(jV[i][j]=V[i-1][j];
else
V[i][j]=max(V[i-1][j],V[i-1][j-w[i]]+v[i]);
}
j=C;
for(i=n-1;i>=0;i--)
{
if(V[i][j]>V[i-1][j])
{
x[i]=1;
j=j-w[i];
}
else
x[i]=0;
}
printf("选中的物品是:
\n");
for(i=0;iprintf("%d",x[i]);
printf("\n");
returnV[n-1][C];
}
voidmain()
{
ints;//获得的最大价值
intw[15];//物品的重量
intv[15];//物品的价值
intx[15];//物品的选取状态
intn,i;
intC;//背包最大容量
n=5;
printf("请输入背包的最大容量:
\n");
scanf("%d",&C);
printf("输入物品数:
\n");
scanf("%d",&n);
printf("请分别输入物品的重量:
\n");
for(i=0;i