HDU - 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包的复杂度如何一步步降为n*W)

题目链接https://vjudge.net/contest/346265#problem/H
在这里插入图片描述
这是一道有个数限制的背包问题。
对于每一种物品至多选一个或者选任意个的问题我们已经可以在**O(nW)**时间内求解。
对于此问题,时间复杂度O(nWc)
代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int p[101],h[102],c[101];
int dp[101];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)
            scanf("%d%d%d",&p[i],&h[i],&c[i]);
        for(int i=0; i<m; i++)
        {
            for(int j=n; j>=0; j--)
            {
                for(int k=0; k<=c[i]; k++)
                {
                    if(k*p[i]>j)break;
                    dp[j]=max(dp[j],dp[j-k*p[i]]+k*h[i]);
                }
            }
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}

优化
每一种物品至多选mi个。
mi=1+2+…+2^k+a(0<=a<2 ^(k+1))
由于1,2…,2^(k+1)的组合可以表示0~2 ^(k+1)-1的所有整数,因此1,2…2 ^k,a可以表示0~m的所有整数。
我们把mi个重量和价值分别为wi和vi的物品,看成重量和价值分别为wi * x,vi * x(x=1,2…2 ^k,a)的k+2个物品
这样物品的总个数就变为(nlogm)个,使用一般的01背包可以在O(nWlogm)时间内求解。
代码

#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,m;
int p[N],h[N],c[N];
int dp[N];
int main()
{		
	int T;
    scanf("%d",&T);
    while(T--)
    {
    	memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)
            scanf("%d%d%d",&p[i],&h[i],&c[i]);
        for(int i=0; i<m; i++)
        {
            int num=c[i];
            for(int k=1; num>0; k<<=1)
            {
                int mul=min(k,num);
                for(int j=n; j>=p[i]*mul; j--)
                    dp[j]=max(dp[j],dp[j-p[i]*mul]+h[i]*mul);
                num-=mul;
            }
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}
        
发布了165 篇原创文章 · 获赞 6 · 访问量 5060

猜你喜欢

转载自blog.csdn.net/lylzsx20172018/article/details/103377312
今日推荐