动态规划,是常规的解决问题的一种方法,能解决的问题具有子问题的性质,即将大问题化成小问题进行分析解决,动态规划最重要的无非两点:状态和状态转移方程。所谓状态,指的是动态规划在化成每一个小问题时的状态,而状态转移方程,则是动态规划的关键:即将大问题化成小问题的方程:每个小问题的最优解都可以由这个方程得到,而大问题的最优解则是建立在小问题的最优解的基础上,下面我将通过一道经典题目来方便理解动态规划。
经典动规问题之0-1背包问题:
小明在旅行时偶然碰见一些钻石(假设有n个),而他只有一个背包,这个背包具有最大能承受的重量W,这n个钻石都有价值v和重量w,显而易见,钻石不能分解,所以小明对每个钻石只能选择带或不带每个钻石,请问:小明最大能带的钻石的价值是多大?
分析:很明显,这是一个动态规划问题:因为它能分解成小问题最优解--每个钻石带或不带,因此,我们只需要比较带了当前钻石以后的价值和不带当前钻石的价值哪个大,再考虑带还是不带当前钻石:没错,这就是我们此题的状态。假设我们用m[i][j]来保存有i个物品,最大承受重量为j时的最大价值,w[i],v[i]表示第i个物品的重量和价值那么我们的1状态转移方程又上面得:m[i][j] = max(m[i - 1][j],m[i - 1][j - w[i]] + v[i] ),分别表示不带当前物品的的价值和带上当前物品的价值,两者取大则为问题最优解,而最有优解也可以化为这样的方程,从而求得最优解的最优解,则得到的一定是最优解,接下来再上代码,就很好理解了:
/*0-1背包问题*/
#include <stdio.h>
int m[1000],v[1000],sum[1000][1000];//m[i]为每一个物品的重量,v[i]为每一个物品的价值,sum[i]用来存放背包可存放的最大价值
int max(int a,int b)
{
return (a > b ? a : b);
}
int main(void)
{
int i,j,M,n;//M为背包能装的最大重量,n为可选择物品个数
while (scanf ("%d%d",&n,&M) != EOF)//输入可选择的物品个数
{
for (i = 0;i < n;i++)
{
scanf ("%d%d",&m[i],&v[i]);//输入每个物品的重量和价值
}
for (i = 0;i <= n;i++)
{
for (j = 0;j <= M;j++)
{
sum[i][j] = i == 0 ? 0 : sum[i - 1][j];//先将大问题表示为不带当前物品时的价值
if(i > 0 && j >= m[i - 1])
{
sum[i][j] = max(sum[i][j],sum[i - 1][j - m[i - 1]] + v[i - 1]);//再将此时的价值和带上当前物品时的价值进行比较,两者取大
}
}
}
printf ("%d\n",sum[n][M]);//输出有n个物品,最大承受重量为M时的最大价值
}
return 0;
}/*未完待续*/