hdu 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活(多重背包)

题意:给出经费的最大值n,再给出   种类m和每种的袋数c、价格p、重量h,求能买大米的最大重量

思路:每种物品有一个固定的次数上限。为多重背包问题。转换为01背包来做

以下方法,均为转化为01背包来做

思路1:物品不摊开,选取每一种时,进行讨论,(相当于竖着填背包v的一列,一列一列的填

码1:kj

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[110];
int main()
{
    int i,j,k,tem;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[110],v[110];//花费,价值
    int num[110];//每种物品个数
    int tc,tv;//拆分时物品花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&_v,&n);
        memset(dp,0,sizeof(dp));

        for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
        //////
        for(i=1; i<=n; ++i)
            for(k=_v; k>=c[i]; --k)
                for(j=1; j<=num[i]&&j*c[i]<=k; ++j)//此处比01背包多了一层循环
                {
                    tc=j*c[i];
                    tv=j*v[i];
                    tem=dp[k-tc]+tv;
                    if(tem>dp[k])dp[k]=tem;
                }
        //
        printf("%d
",dp[_v]);
    }
    return 0;
}
View Code


思路2:物品摊开,然后处理01背包(相当于横着填物品的一行,一行一行的填

2.1朴素拆分,把每种物品展开,既有n件a物品,则进行n件是否选取操作

码2:jk

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[110];
int main()
{
    int i,j,k,tem;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[110],v[110];//花费,价值
    int num[110];//每种物品个数
    int tc,tv;//拆分时物品花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&_v,&n);
        memset(dp,0,sizeof(dp));

        for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
        //////
        for(i=1; i<=n; ++i)
            for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)//此处比01背包多了一层循环
                for(k=_v; k>=j*c[i]; --k)
                {
                    tem=dp[k-c[i]]+v[i];
                    if(tem>dp[k])dp[k]=tem;
                }
        //
        printf("%d
",dp[_v]);
    }
    return 0;
}
View Code


hint:2.1代码与思路1 代码 只是循环次序不一样而已,实际是一样的操作。(并且时间复杂度相同,略麻烦。
因为
思路1:
for(k=_v; k>=c[i]; --k)
      for(j=1; j<=num[i]&&j*c[i]<=k; ++j)
思路2:
for(j=1; j<=num[i]&&j*c[i]<=_v; ++j)
      for(k=_v; k>=j*c[i]; --k)

大体看一下这俩循环,

这两个循环做的循环次数是相同的,也就是说,计算次数相同,时间复杂度相同


2.2  二进制拆分,有n件a物品,则拆成 1,2,4,8,...,q 这样,即 1+2+4+...+q=n

码3:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[110];
int main()
{
    int i,j,k,tem;
    int t,n,_v;//测试用例个数,物品种类,背包大小
    int c[110],v[110];//花费,价值
    int num[110];//每种物品个数
    int tc,tv;//拆分时物品花费,价值
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&_v,&n);
        memset(dp,0,sizeof(dp));

        for(i=1; i<=n; ++i)scanf("%d%d%d",&c[i],&v[i],&num[i]);
        //////
        for(i=1; i<=n; ++i)
        {
            for(j=1; j<=num[i]; num[i]=num[i]-j,j=j*2)//此处比01背包多了一层循环
            {
                tc=j*c[i];//拆分后物品花费
                tv=j*v[i];//
                for(k=_v; k>=tc; --k)
                {
                    tem=dp[k-tc]+tv;
                    if(tem>dp[k])dp[k]=tem;
                }
            }
            if(num[i]>0) //如果还有物品,num[i] 即相当于 1+2+4+...+q  中的 q
            {
                tc=num[i]*c[i];
                tv=num[i]*v[i];
                for(k=_v; k>=tc; --k)
                {
                    tem=dp[k-tc]+tv;
                    if(tem>dp[k])dp[k]=tem;
                }
            }
        }
    //
        printf("%d
",dp[_v]);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/gongpixin/p/4739310.html