目录
1 实验名称
动态规划法的应用
2 实验目的
通过本实验,掌握动态规划法解决问题的算法效率分析方法,运用动态规划法解决问题的思想,解决实际问题。
3 实验内容
理解动态规划方法的内涵,并且能够解决实际问题,如:0-1背包问题、最大子段和问题,并对算法进行分析。打印输出。
4 实验设计及实现
4.1 0-1背包问题
#include<stdio.h>
#include<stdlib.h>
int V[100][100];//前i个物品装入容量为j的背包中获得的最大价值
int max(int a,int b){
if(a>=b)
return a;
else return b;
}
int KnapSack(int n,int weight[],int value[],int C){
//填表,其中第一行和第一列全为0,即V(i,0)=V(0,j)=0;
for(int i=0;i<=n;i++)
V[i][0]=0;
for(int j=0;j<=C;j++)
V[0][j]=0;
//用到的矩阵部分V[n][C] ,下面输出中并不输出 第1行和第1列
printf("编号 重量 价值 "); //菜单栏 1
for(int i=1;i<=C;i++)
printf(" %2d ",i);
printf("\n\n");
for(int i=1;i<=n;i++){
printf("%2d %2d %2d ",i,weight[i-1],value[i-1]); //菜单栏 2 (weight与value都是从0开始存的,所以开始i=1时对应0的位置)
for(int j=1;j<=C;j++){
if(j<weight[i-1]){ //包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的
V[i][j]=V[i-1][j];
printf("%2d ",V[i][j]);
}
else{ //还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个
V[i][j]=max(V[i-1][j],V[i-1][j-weight[i-1]]+value[i-1]);
printf("%2d ",V[i][j]);
}
}
printf("\n");
}
return V[n][C];
}
void Judge(int C,int n,int weight[]){ //判断哪些物品被选中
int j=C;
int *state=(int *)malloc(n*sizeof(int));
for(int i=n;i>=1;i--){
if(V[i][j]>V[i-1][j]){ //如果装了就标记,然后减去相应容量
state[i]=1;
j=j-weight[i-1];
}
else
state[i]=0;
}
printf("选中的物品是:");
for(int i=1;i<=n;i++)
if(state[i]==1)
printf("%d ",i);
printf("\n");
}
int main(){
int n; //物品·数量
int Capacity;//背包最大容量
printf("请输入背包的最大容量:");
scanf("%d",&Capacity);
printf("输入物品数:");
scanf("%d",&n);
int *weight=(int *)malloc(n*sizeof(int));//物品的重量
int *value=(int *)malloc(n*sizeof(int)); //物品的价值
printf("请分别输入物品的重量:");
for(int i=0;i<n;i++)
scanf("%d",&weight[i]);
printf("请分别输入物品的价值:");
for(int i=0;i<n;i++)
scanf("%d",&value[i]);
int s=KnapSack(n,weight,value,Capacity); //获得的最大价值
Judge(Capacity,n,weight); //判断那些物品被选择
printf("最大物品价值为: ");
printf("%d\n",s);
return 0;
}
4.2 最大子段和问题
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
//暴力枚举法
int MaxSubSum_Violent(int *a,int n,int& besti,int& bestj){
int sum=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
int thissum=0;
for(int k=i;k<=j;k++){
thissum+=a[k];
}
if(thissum>sum){
sum=thissum;
besti=i;
bestj=j;
}
}
}
return sum;
}
//优化枚举法
int MaxSubSum_Optimized(int *a,int n,int &besti,int &bestj){
int sum=0;
for(int i=0;i<n;i++){
int thissum=0;
for(int j=i;j<n;j++){
thissum+=a[j];
if(thissum>sum){
sum=thissum;
besti=i;
bestj=j;
}
}
}
return sum;
}
//分治法
int CrossSubArray(int *a,int left,int mid,int right){
int leftsumMax=0,rightsumMax=0,temp=0;;
for(int i=mid;i>=left;i--){
temp+=a[i];
if(temp>=leftsumMax){
leftsumMax=temp;
}
}
temp=0;
for(int j=mid+1;j<=right;j++){
temp+=a[j];
if(temp>=rightsumMax){
rightsumMax=temp;
}
}
int crosssum=leftsumMax+rightsumMax;
return crosssum;
}
int MaxSubSum_DivideConquer(int *a,int left,int right){
int sum=0;
if(left==right){
sum=a[left];
}else{
int mid=(left+right)/2;
int leftsum=MaxSubSum_DivideConquer(a,left,mid);
int rightsum=MaxSubSum_DivideConquer(a,mid+1,right);
int crosssum=CrossSubArray(a,left,mid,right);
if(leftsum>=rightsum&&leftsum>=crosssum){
sum=leftsum;
}else if(rightsum>=leftsum&&rightsum>=crosssum){
sum=rightsum;
}else if(crosssum>=rightsum&&crosssum>=leftsum){
sum=crosssum;
}
}
return sum;
}
//动态规划
int MaxSubSum_DynamicProgram(int *a,int n){
int Max_sum=0;
int *f=(int *)malloc(sizeof(int)*(n+1));
for(int i=0;i<n;i++){
f[i]=a[i];
}
for(int i=1;i<=n;i++){
if(f[i-1]+a[i]>=a[i]) f[i]=f[i-1]+a[i];
if(f[i-1]+a[i]<a[i]) f[i]=a[i];
if(f[i]>=Max_sum) Max_sum=f[i];
}
// for(int i=1;i<=n;i++){
// if(f[i-1]>=0) f[i]=f[i-1]+a[i];
// if(f[i-1]<0) f[i]=a[i];
// if(f[i]>=Max_sum) Max_sum=f[i];
// }
return Max_sum;
}
int main(){
int n=0;
printf("请输入原数组的长度:");
scanf("%d",&n);
int *a=(int*)malloc(sizeof(int)*(n+1));
if(!a) exit(-2);
puts("请输入原数组:");
for(int i=0;i<n;i++){
scanf("%d",a+i);
}
int besti,bestj;
puts("使用暴力枚举法的结果是");
printf("最大子段和为:%d\n",MaxSubSum_Violent(a,n,besti,bestj));
puts("对应的子数组为:");
for(int i=besti;i<=bestj;i++){
printf("%d ",a[i]);
}
putchar('\n');
puts("--------------------------------");
puts("使用优化枚举法的结果是");
printf("最大子段和为:%d\n",MaxSubSum_Optimized(a,n,besti,bestj));
puts("对应的子数组为:");
for(int i=besti;i<=bestj;i++){
printf("%d ",a[i]);
}
putchar('\n');
puts("--------------------------------");
puts("使用分治法的结果是");
printf("最大子段和为:%d\n",MaxSubSum_DivideConquer(a,0,n));
puts("--------------------------------");
puts("使用动态规划法的结果是");
printf("最大子段和为:%d\n",MaxSubSum_DynamicProgram(a,n));
}