动态规划算法 背包问题
printf("%5d%5d\n",w[i-1],v[i-1]); i=j; } }
void main( ) { int i,j;
printf("输入物品种数:"); scanf("%d",&n); printf("输入每种物品的重量与价值:\n"); for (i=0; i<n; i++)
scanf("%d%d",&w[i],&v[i]); printf("输入背包的总重量:\n"); scanf("%d",&c); knapsack(); disp(); printf("最大价值=%d\n",m[n][c]); for (i=0; i<=n; i++) { for (j=0; j<=c; j++)
}
void main( ) { int i,j;
printf("输入物品种数:"); scanf("%d",&n); printf("输入每种物品的重量与价值:\n"); for (i=1; i<=n; i++)
scanf("%d%d",&w[i],&v[i]); printf("输入背包的总重量:\n"); scanf("%d",&c); knapsack(); disp(); printf("最大价值=%d\n",m[0][c]); for (i=1; i<=n; i++) { for (j=0; j<=c; j++)
0
i=0 或者 j=0
m[i][j]= m[i-1][j]
j>0且j<w[i]
Max(m[i-1][j], m[i-1][j-w[i] ]+v[i] ) i>0且j>=w[i]
//程序2:动态规划法 #include <stdio.h> #define MAX 20 int n,c,w[MAX],v[MAX],m[MAX][MAX]={0}; void knapsack() { int i,j;
for (i=1; i<=n; i++) if ( m[i][c]!=m[i+1][c] ) printf("%5d%5d\n",w[i],v[i]);
}
void knapsack() { int i,j;
for (j=w[n]; j<=c; j++) m[n][j]=v[n];
for (i=n-1; i>=1;i--) for (j=w[i]; j<=c; j++) if ( m[i+1][j]>m[i+1][j-w[i]]+v[i] ) m[i][j]=m[i+1][j]; else m[i][j]=m[i+1][j-w[i]]+v[i];
}
//显示所取的物品及其重量(其中一个解) //对数组m的最后一列检查来求解 void disp( ) { int i,j;
i=n; while ( m[i][c]==m[i-1][c] ) i--; while (i>0) { j=i-1;
while (m[i][c]-m[j][c]!=v[i-1] && j>0) j--;
for (i=1; i<=n; i++) for (j=1; j<=c; j++) { m[i][j]=m[i-1][j]; if ( j>=w[i-1] && m[i-1][j-w[i-1]]+v[i-1]> m[i][j] ) m[i][j]=m[i-1][j-w[i-1]]+v[i-1]; }
printf("%3d",m[i][j]); printf("\n"); } }
算法思想2:设m[i][j]用来表示从前i项物品中区 取出装入体积为j的背包的物品的最大价值。其中i的范 围为1到n,其中j的范围为0到c,程序要寻求的解为 m[n][c]。可以清楚地发现:
①m[0][j]对所有的j的值为0, m[i][0]对所有的i的 值为0。 ②当前的体积j大于等于w[i]时, m[i][j]是 下面两个量的最大值:m[i-1][j] 和 m[i-1][j-w[i]]+v[i] ③当前的体积j小于w[i]时,m[i][j]等于m[i-1][j]
例:输出Fibonacii数列的第n项的递归算法
#include <stdio.h> int fib(int n) { if (n<=1) return 1;
else return fib(n-1)+fib(n-2); } void main( ) { int n;
scanf("%d",&n); printf("%d\n" ,fib( n ) ); }
a[1]=a[2]=1; for (i=3; i<=n; i++)
a[i]=a[i-1]+a[i-2]; return a[n]; } void main( ) { int n; scanf("%d",&n); printf("%d\n" ,fib( n ) ); }
例1:0-1背包问题
有一个负重能力为m的背包和不同价值v[i]、不同 重量w[i]的物品n件。在不超过负重能力的前提下, 从这n件物品中任意选择物品,使这些物品的价值之 和最大。
在上面的递归算法中存在多次计算同一个子问 题,如:fib(2)。如果能将这样的子问题的解用数组 保存起来,即可以加快求解的过程,即采用动态规 划方法。
//输出Fibonacii数列的第n项的动态规划算法
#include <stdio.h> #define MAX 50 int fib(int n) { int i,a[MAX];
m[i][j]=
m[i+1][j] Max(m[i+1][j], m[i+1][j-w[i] ]+v[i] )
当j<w[i] 当j>=w[i]
m[n][j]=
v[n] 0
当j>=w[n] 当j>=0 并且 j< w[n]
//程序1:动态规划法 #include <stdio.h> #define MAX 20 int n,c,w[MAX],v[MAX],m[MAX][MAX]={0}; void disp( ) { int i;
物品 1
2
3
4
重量 5
3
2
1
价值 4
4Hale Waihona Puke 31算法思想1:设m[i][j]用来表示从第i项物品开始 到第n项物品中区取出装入体积为j的背包的物品的最 大价值。其中i的范围为1到n,其中j的范围为0到c, 程序要寻求的解为m[1][c]。可以发现:
①m[n][j] 在当j>=0并且j< w[n] 时等于0,否则等 于v[n] ②当前的背包容量j大于等于物品重量w[i]时, m[i][j]是下面两个量的最大值:m[i+1][j] 和 m[i+1][ jw[i] ]+v[i] ③当前的背包容量j小于物品重量w[i]时, m[i][j]等于m[i+1][j]。
printf("%3d",m[i][j]); printf("\n"); } }
动态规划算法原理
将待求解的问题分解成若干个相互联系 的子问题,先求解子问题,然后从这些子问 题的解得到原问题的解;对于重复出现的子 问题,只在第一次遇到的时候对它进行求解, 并把答案保存起来。
为了不去求解相同的子问题,引入一个数 组,把所有子问题的解存于该数组中,这就 是动态规划所采用的基本方法。动态规划采 用由下至上(Bottom-Up) 计算策略。