本次学习背包问题的样例题目是USACO的分数膨胀:http://www.nocow.cn/index.php/Translate:USACO/inflate
看了解释之后,兴致勃勃的开始写,写到后面发现不对劲,因为无法处理包容多个不同种背包的情况。
动态规划的思想很新颖,总是能把看似简单,但是仔细一样又会觉得没辙的问题,解决的很顺利。理解背包问题的动规思想花了我一点时间。同时又设想到,要是自己要设计这么一个动规的思路,看来还需要花些功夫了。嘿嘿。
再后来直接看代码,发现代码很短,但是比较难懂。
于是看代码的理解又花了我一些功夫。网上有很多资料解释动规问题,但是看了之后,意思是懂了。
措施是将把所给物品放进背包的问题分解成将某一件物品放进背包的问题。
放或者不放,就两种情况。
按照官方的解释,就是设立一个函数f[i,j],在选择前i种物品,容量限制是j的情况下,所能获得的最大价值。
f[i,j] = max{ f[i-1,j], f[i,j-mti]+ pti}
f[i,j] :选择前i种题目,时间不超过j的条件下,获得的最大分数。
== f[i-1,j] :不放入第i种题目,前i-1种题目在j最大时间的限制下,获得的最大分数;
== f[i,j-mti]+ pti:放入第i种题目后,转化成,把前i-1种题目在j-mti的时间的限制下,能获得的最大分数,再加上第i种题目加入后能够获得的分值。(即当前时限中已经有第i种题目的时间占位,剩下的时间再求最优解)
即选择放 与 不放 两种选择的价值最大化情况。
但是在代码实现的时候,又有点不一样,这个就让我有点懵了。不过也可以慢慢理解,
#include <stdio.h>
#define MAXSIZE 10001
#define max(a,b) (((a) > (b)) ? (a) : (b))
int MaxGet[MAXSIZE];
int main() {
int i,j,M,N;
int ProblemCost,ProblemGet;
scanf("%d%d",&M,&N);
for(i = 0;i < N;i ++){
scanf("%d %d",&ProblemGet,&ProblemCost);
if(ProblemGet <= MaxGet[ProblemCost])
continue;
//printf("cost:%d-->\n",ProblemCost);
for(j = ProblemCost;j <= M;j ++)
{
MaxGet[j] = max(MaxGet[j],MaxGet[j-ProblemCost] + ProblemGet);
//printf("[%d]:%d,",j,MaxGet[j] );
}
}
printf("%d\n",MaxGet[M]);
return 0;
}
对上述问题:
for(j = ProblemCost;j <= M;j ++)
{
MaxGet[j] = max(MaxGet[j],MaxGet[j-ProblemCost] + ProblemGet);
//printf("[%d]:%d,",j,MaxGet[j] );
}
这处的解释如下:
maxget[j]表示在时限j的条件下,添加一个题目i,与不添加一个题目i,相比,能够获得的最多分数。
横向看:随着时限j的增加,当j=2*j的时候,就成了比较增加第二个j与增加一个j的情况下,能够获得的最大分数。
纵向看:当时限ji=jk的时候,同样也是通过ji的自增,就成了比较,增加一个jk与不增加ji的情况下,能够获得的最大分数。
同样,当jk做东的时候,就是比较增加一个ji与不增加jk的情况下,能够获得的最大分数。
同样,我还尝试过,j或者ji,只增加ProblemCost个,
即 j += ProblemCost;但是不行,因为当比较到不是整倍数的情况的时候,将无法进行正确的比较。
如:有两个题型i和k。ProblemCosti=121,ProblemCostk=36.总时限是300。i和k就永远比不到一块去,而且,也永远记录不到时限300的情况。即无法处理不能完整消耗总时限的情况,出现零头的情况。