动态规划法:Dynamic Programming,DP
就像分治法一样可以将问题分为若干个子问题,但是呢,用动规法处理的子问题往往不是独立的,也就是说,子问题之间有相互联系,会相互影响的,参考Fibonacci数列,子问题之间是有联系的。
一般来说,子问题的重叠关系体现在给定问题满足的递推关系中(称谓动态规划函数)
通常来说,动态规划法将每个子问题求解一次并将其保存在一个表格中,当程序需要调用这些数据处理子问题时,只是简单通过查表获得该问题的解,从而避免了算了一次又算了一次。
因为把数据存起来了,就更加方便了。
0/1背包问题:
给定 n 种物品和一个容量为 C 的背包,物品 i 的重量是 w,其价值为 v 。
问:应该如何选择装入背包的物品,使得装入背包中的物品的总价值最大?
首先我们来滤清一下思路:
在对该问题的解决中,
该问题有两种潜在状态:
1.
背包容量不足以装入物品,背包不增加价值
2.
背包容量可以装下物品,背包的价值增加了。
第二个潜在状态:
也会出现两种分状态:
1.把第i个物品放入背包,背包的价值等于把前i-1个物品装入容量j-w(这个w指装入物体容量)获得的价值加上第i个物品的价值。
2.第i个物体的确可以装入背包,但我耍赖,不装,此时等于把i-1个物体装入背包获得的价值。
而要选最优解,当然要选两者中的最大值。
代码如下:
前缀:
#include<stdio.h>
#define N 10
#define Q 100
int V[N+1][Q+1];
int KnapSack(int n,int w[],int v[],int C);
主函数:
int main()
{
int w[N],v[N],C,maxValue;
int n,i;
printf("输入物体的个数:\n");
scanf("%d",&n);
printf("输入物体的重量:\n");
for(i=0;i<n;i++)
scanf("%d",&w[i]);
printf("请输入物体的价值:\n");
for(i=0;i<n;i++)
scanf("%d",&v[i]);
printf("请输入背包的容量:\n");
scanf("%d",&C);
maxValue=KnapSack(n,w,v,C);
printf("获得的最大价值为:%d\n",maxValue);
return 0;
}
函数:
int KnapSack(int n,int w[],int v[],int C)
{
int i,j;
for(j=0;j<=C;j++)
V[0][j]=0;
for(i=0;i<=n;i++)
V[i][0]=0;
for(i=1;i<=n;i++)
for(j=1;j<=C;j++)
{
if(j<w[i-1])
V[i][j]=V[i-1][j];
else if(V[i-1][j]>V[i-1][j-w[i-1]]+v[i-1])
V[i][j]=V[i-1][j];
else
V[i][j]=V[i-1][j-w[i-1]]+v[i-1];
}
return V[n][C];
}
具体实验如下: