背包问题(一)

详细的参见背包九讲,地址:http://download.csdn.net/detail/u014007510/7258089

i表示前i件物品,j表示空间大小

01背包:每件物品可放一次,状态转移方程:

二维的:       f[i][j]=max(f[i-1][j],f[i-1][j-cost[i]]+worth[i]),放与不放中取最大的

先写成i行j列的矩阵;
观察到:f[i][j]的取值只与f[i-1]层的数据有关,每次更新第j列的数据就行了,那么可以将二维的动态转移方程写成一维的 形式;
f[j]=max(f[j],f[j-cost[i]]+worth[i])   ①
ps:    其实就是把f里面的二维i都去掉,对比上面的方程会发现神奇之处,暂且留个小问题下面讲
01背包是核心思想必须要完全理解它。

完全背包:
二维的:   f[i][j]=max(f[i-1][j],f[i-k][j-k*cost[i]]+k*worth[i])   或者  f[i][j]=max(f[i-k][j-k*cost[i]]+k*worth[i]) 
ps: 转换成01背包来做了,k的可能取值: 0<=V-k*cost[i]<=V,枚举k就可以了。
上面的动态转移方程看起来比较复杂,而且也不太美,有没有更简洁的方程呢?
简单的二维方程: f[i][j]=max[f[i-1][j],f[i][j-cost[i]]+worth[i]]  
这个方程比较有意思,f[i-1][j]表示在j的空间中放前i-1件的物品可达到的最大价值,f[i][j-cost[i]]+worth[i]表示在j-cost[i]的空间里放前i件物品可达到的最大价值的基础上 放一个第i件物品,f[i][j]的取值必然是由这两种情况得来的!其实这个方程就是:在可能放多个与不放中取最大的。
可能放多个的表示方法:就是在可能已放(即f[i][j-cost[i]])的基础上再放一个worth[i]。(讲的清楚吧^_^)
一维的: f[j]=max(f[j],f[j-cost[i]]+worth[i])       ②同样是在放与不放中取最大的


呵呵,看到这儿请比较①②的,看起来是完全一样的,那么应该怎么区分他们呢?
注意:①中max里的f[j]表示的是放前i-1件的最大值,②中max里的f[j]表示的是放前i件的最大值;
实现:
①:
void ZO(int V,int C,int W)//V表示整个空间的大小,C指第i种物品的大小,W表示第i种物品的价值
{
    for(int v=V;v>=C;v--)//倒序更新,保证F[v]是前i-1件的最大值
    {
        F[v]=max(F[v],F[v-C]+W);
    }
}


②对每个i执行
void CO(int V,int C,int W)
{
    for(int v=C;v<=V;v++)//顺序更新,保证F[v]是前i件的最大值
        F[v]=max(F[v],F[v-C]+W);
}


多重背包:
二维的:   f[i][j]=max(f[i-1][j],f[i-k][j-k*cost[i]]+k*worth[i])   或者  f[i][j]=max(f[i-k][j-k*cost[i]]+k*worth[i]) 
ps: 和完全背包类似,0<=V-k*cost[i]<=V且0<=k<=num[i]
有了前面的基础可以想到:把第i件做多取num[i]件理解为有num[i]件价值为worth[i]的物品,再用01背包做就行了。
但是有没有更高效的方法呢?
先看这个问题:一个数字n可以写成n=2^0+2^1+2^3+...+2^i+k,1<=2^i<=n,0<=k<=n-2^i
以19为例:写成10011>1111,进而19=10011=1+10+100+1000+(10011-1-10-100-1000)=1+2+4+8+4
通过上面的简单非严格说明可以知道,n=2^0+2^1+2^3+...+2^i+k,1<=2^i<=n,0<=k<=n-2^i
回到原问题,
将num[i]件物品分解为2^0件的组合体+2^1件的组合体+2^3件的组合体+...+2^件的组合体i+k件的组合体。这种方法比前一种好多了。
实现:
对每个i
void MU(int V,int C,int W,int M)
{
    if(C*M>=V)//相当于完全背包
        CO(V,C,W);
    else
    {
        int k=1;
        while(k<M)
        {
            ZO(V,k*C,k*W);//分解为01背包
            M=M-k;
            k=2*k;
        }
        ZO(V,M*C,M*W);
    }
}
混合背包问题:
其实就是多重背包问题,不过num[i]的取值有区别罢了:属于01背包的num[i]=1,属于完全背包的num[i]=(V/cost[i]+1)。
当然也可以这么做

for i=1..N

    if 第i件物品属于01背包

        ZO(V,cost[i],worth[i])

    elseif 第i件物品属于完全背包

        CO(V,cost[i],worth[i])

    else if 第i件物品属于多重背包

        MU(V,cost[i],worth[i],num[i])



最后初始化问题:
①恰好装满:f[0]=0,f[1,2,3.....n]=- ∞,理解:对0,1,2...n大的空间都什么也不装,只有0可以装,其他都是非法的
②不用恰好装满:f[0,1,2...n]=0,理解,对0,1,2...n大的空间都什么也不装,都是合法的,都可为0
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值