1.动态规划是用空间换取时间的一种方法的抽象,其关键是发现子问题和记录其结果,然后利用结果减轻计算量。0/1背包问题就应用了动态规划的思想。
2.0/1背包问题:一个背包最多可以装M公斤的东西,现在有N件物品,分别为X1,X2...Xn,它们的重量分别为M1,M2...Mn,它们的价值分别为V1,V2...Vn,若某种物品只有一件,它可以放进包中,也可以不放进包中,问如何选取物品可以使得包中的物品的价值最大。
思路:目标函数为 max∑XiVi(i=1,2...n),约束条件为 ∑XiMi<=M(i=1,2...n) ,其中Xi={0,1}。可以将背包问题的求解过程看做是一系列的决策过程,即决定哪些东西放入背包,哪些不放入背包,如果一个问题的最优解包含物品n,即Xn=1,那么其余X1,X2...Xn-1构成子问题,求它们在中路 M-Mn 中的最优解。
f(N,M)=max{f(N-1,M), f(N-1,M-Mn)+Mn}
我们以最大容量M=10和物品个数N=3为例,三个物品重量分别为3,4,5,物品价值分别为4,5,6,c[i][j]保存当允许重量为j时,从前i个物品中选择可以得到的最大价值。第一行均为0,因为没有物品可以选择;第二行当j=0,1,2时c[i][j]=0,因为可以允许的重量小于任何物品的重量,当j=3时,可以将第一个物品装进去了,c[i][j] = 4,这一行剩下的部分均为4,因为只有一个物品可以选择;第三行,当j=3时,表示可以从1,2两个物品中选择,但是总重量不可以超过3,所以c[i][j] = 4,但是当j=4时,我们可以选择有第2个物品(此时最大价值为c[i-1][j-4]+5=c[2][0]+5=5)或没有第2个物品(此时最大价值为c[i-1][j]=c[2][4]=4),所以最后的结果为5,依次类推。
我们将在总重量不超过M的前提下,前i种物品的总价格所能达到的最高值定义为A(i, M)。
A(i, M)的递推关系为:
A(0, M) = 0
A(i, 0) = 0
如果wi > M, A(i, M) = A(i - 1, M)
如果wi ≤ M, A(i, M) = max { A(i - 1, M), Vi + A(i - 1, M - wi) }
//假设M=100,N=10,也就是10个物品、总重量最大为100
for(i=0;i<10;i++)
for(j=0;j<100;j++)
c[i][j]=0;/*初始化数组*/
for(i=1;i<N;i++)
for(j=1;j<M;j++)
{
if(M[i]<=j) /*如果当前物品的重量小于背包重量*/
{
if(V[i]+c[i-1][j-V[i]]>c[i-1][j])
/*如果本物品的价值加上背包剩下的空间能放的物品的价值*/
/*大于上一次选择的最佳方案则更新c[i][j]*/
c[i][j]=V[i]+c[i-1][j-V[i]];
else
c[i][j]=c[i-1][j];
}
else c[i][j]=c[i-1][j];
}
3.背包问题的应用
编程之美——数组分割问题:有一个没有排序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近。
isOk[i][v]表示是否可以找到i个数,使得它们之和为v
初始化:isOk[0][0]=true;
isOk[i][v]=false; i>0,v>0
for(k = 1,k <= 2*n; k++){
for(i = 1;(i <= k && i <=n);i++){
for(v = 1;v < sum/2; v++)
if(v >= arr[k] && isOk[i-1][v-arr[k])
isOk[i][v] = true;
}
}
参考资料:
http://blog.csdn.net/jhj735412/article/details/7401463
http://blog.csdn.net/yeepom/article/details/8712224