入门多重背包-HDU2844

题目链接

给你若干种面值的硬币,每种硬币有多个,给你一个数字m,问你能用给你的硬币组合出和为1-m之间的几种情况,例如1个两元硬币 2个一元硬币 m=3时 就能组合出来1 2 3 答案就是三 ;m=4时,答案就是4 ;m=5时答案还是4。(难得我解释了一次题意,hh)

如果会01背包与完全背包,多重背包其实看代码就能理解,难的地方只有一个就是那个二进制优化,昨晚问马姐姐问到十一点多终于懂了,感谢马姐姐,马仙女~

多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:

f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]  |  0<=k<=n[i]}

多重背包代码:

void pd(int m,int v,int w,int num){   //m钱数
    if(num*v>=m){
        pall(m,v,w);
        return;
    }
    for(int i=1;i<=num;i<<=1){
        p01(m,i*v,i*w);
        num-=i;
    }
    if(num) p01(m,num*v,num*w);
}

其实也没多长对吧,别的不解释,就只说说二进制优化吧

再说二进制优化之前,我们要先明白笨方法是什么,这里给出

笨方法的代码:

void pd(int m,int v,int w,int num){
    if(num*v>=m){
        pall(m,v,w);
        return;
    }
    for(int i=1;i<=num;i++){
        p01(m,i*v,i*w);
    }
}

首先我要你搞明白一件事,用这个笨方法,也能出结果,只是效率不高,我昨晚就是没弄明白这个,所以耽误了好久。

之所以这总方法效率不高,在于,他对每一个i,也就是放置同种物品的个数,都从1到num遍历了,我们这道题物品个数i最大可能是1000的,可想而知,对于每一个i都要进行一次01背包,效率肯定不高,,那么为什么二进制优化中,只取了某几个i(比如说num=10 的时候,i只取了1 2 4 3这四个数)来进行,却能达到和“笨方法”一样的效果呢?

显然,为了保证背包动归结果正确,我们要考虑在背包内放入1 , 2 3 4,,,num件某物品时的这num种情况所对应背包的状态,缺一不可。我们的笨方法就很直观的达到了这种目的,下面分析“聪明办法”为什么能达到这种目的:

我们以10为例,第一轮01背包i=1,如果i=1也不行那后面i肯定也都不行说明放不进去

如果i=1可以,下一次i=2时,经过了01背包中的状态转移方程:f[k]=max(f[k],f[i-v]+w) (k从m到v循环)  之后,对于每一个f[k]可能有三种情况,那就是:

1,i=1时只放一个物品时的物品被拿出来了,放进去了两个物品

2,i=1时放进去的物品没拿出来,又放进去两个物品

3,i=1时放进去的物品没拿出来,i=2时的两个物品也没拿出来

这三种情况可能不会都出现,如果没有出现,那就说明这种情况不会是解~

以此类推,之后i=4 ,3

可以把放进去 1 2 3 4 5 6 7 8 9 10个物品的情况统统考虑一遍,从而达到了和“笨方法”一样的效果。

This is it.

完整代码:

#include <iostream>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=0x3f3f3f3f;
int n,m;
int f[100010],a[110],c[110]; 

void p01(int m,int v,int w){
    for(int i=m;i>=w;i--){
        f[i]=max(f[i],f[i-w]+v);
    }    
}

void pall(int m,int v,int w){
    for(int i=w;i<=m;i++){
        f[i]=max(f[i],f[i-w]+v);
    }
}

void pd(int m,int v,int w,int num){
    if(num*v>=m){
        pall(m,v,w);
        return;
    }
    for(int i=1;i<=num;i<<=1){
        p01(m,i*v,i*w);
        num-=i;
    }
    if(num) p01(m,num*v,num*w);
}

int main()
{
    freopen("in.txt","r",stdin); 
    while(cin>>n>>m){
        memset(f,0,sizeof(f));
        if(n==0) break;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>c[i];
        for(int i=1;i<=m+1;i++)   f[i]=-maxn;
        f[0]=0;
        for(int i=1;i<=n;i++){
            pd(m,a[i],a[i],c[i]);
        }
        int ans=0;
        for(int i=1;i<=m;i++){
            if(f[i]>0) ans++;
        }
        cout<<ans<<endl;
    }
    return 0;
}
View Code
柳暗花明又一村
原文地址:https://www.cnblogs.com/ucandoit/p/8808791.html