今天遇到一题poj2184,大概思路是01背包dp之后把符合要求的最优解统计出来。但是在解01背包的时候遇到一个问题是体积有负数,这样在dp的过程中会遇到两个问题:循环的时候超出体积的范围;压缩空间的时候状态转移方程:dp[v]=max(dp[v],dp[v-c[i]]+w[i]),c[i]为负数时v-c[i]>v,这样按一般的循环的方向从大到下会重复计算。
先看第二个问题,在一般的01背包压缩空间的时候,体积的遍历是从大到小,因为dp[v]=max(dp[v],dp[v-c[i]]+w[i]),当前的dp[v]只取决于比自己小的dp[v-c[i]],所以从大到小遍历时每次dp[v-c[i]]和dp[v]都是上一次的状态。
如果体积为负v-c[i]>v,从大到小遍历dp[v-c[i]]是当前物品的状态,不是上一个,这样就会出错,解决的办法是从小到大遍历。
针对第一个问题,在处理的时候将整个数轴平移,使得原来所有可能的情况都为正。
例如这题,首先计算出数据的范围:
一共100组数,从-1000到1000,那么体积的范围就是-100*1000到100*1000。平移之后我们要处理的数据范围就在0到200000,新的原点变成100000。
初始化变成:
- for(int i=0;i<=200000;i++) dp[i]=-INF;
- dp[100000]=0;
循环变成:
- for(int i=1;i<=n;i++)
- {
- if(s[i]>0)
- {
- for(int v=200000;v>=s[i];v--) //从可能的最大值到最小值
- {
- if (dp[v-s[i]]>-INF)
- dp[v]=max(dp[v],dp[v-s[i]]+f[i]);
- }
- }
- else
- {
- for(int v=0;v-s[i]<=200000;v++)
- {
- if (dp[v-s[i]]>-INF)
- dp[v]=max(dp[v],dp[v-s[i]]+f[i]);
- }
- }
- }
计算结果要从100000开始遍历,因为100000相当于原来的0
- int nMax=0;
- for(int v=100000;v<=200000;v++)
- if(dp[v]>=0)
- nMax=max(nMax,dp[v]+v-100000);
这题还有一种解决方法:把物品的体积全部加1000,使它们都大于0,然后dp的时候用一个数组记录dp[i][v]时多加了几个1000,最后在结果里减去就行了。这个做法有个需要注意的地方是状态转移变成:
for(int i=1;i<=n;i++)
for(int v=sum;v>=s[i];v--)//tot记录多加的1000
if(dp[v]-tot[v]*1000 <
dp[v-s[i]]+f[i]-(1+tot[v-s[i]])*1000)
{
dp[v]=dp[v-s[i]]+f[i];
tot[v]=tot[v-s[i]]+1;
}
而不是
- if(dp[v]<dp[v-s[i]]+f[i])
- {
- dp[v]=dp[v-s[i]]+f[i];
- tot[v]=tot[v-s[i]]+1;
- }
这题的第二种解法只是看结题报告粗浅的理解下,动态规划真是非常神奇。。。。
原文来自:
http://blog.csdn.net/actangy/article/details/7433992
致谢~~~~~~!