您的位置:首页 > 其它

poj 2184 01背包变形:体积为负数的处理

2014-01-20 15:31 561 查看
今天遇到一题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。

初始化变成:

[cpp] view
plaincopy

for(int i=0;i<=200000;i++) dp[i]=-INF;



dp[100000]=0;

循环变成:

[cpp] view
plaincopy

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

[cpp] view
plaincopy

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;
                }


而不是

[cpp] view
plaincopy

if(dp[v]<dp[v-s[i]]+f[i])

{

dp[v]=dp[v-s[i]]+f[i];

tot[v]=tot[v-s[i]]+1;

}

因为这时的最大值是某个v+dp[v]-tot[v]*1000,v一定的情况下dp[v]-tot[v]*1000表示的才是最大值,虽然这个最大值的意义不好理解。。

这题的第二种解法只是看结题报告粗浅的理解下,动态规划真是非常神奇。。。。

原文来自:

http://blog.csdn.net/actangy/article/details/7433992

致谢~~~~~~!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: