dp2之多重背包

沉浸在背包的世界里不能自拔…
多重背包在完全背包的基础上升级为每个种类的东西的数目是有限的

http://acm.hdu.edu.cn/showproblem.php?pid=2191
☝多重背包裸题

AC代码

#include<iostream>
using namespace std;
#include<string.h>
#include<algorithm>

int main()
{
    int C;
    int n, m;
    int p[600], h[600], c[600];//原始数据存储
    int w[600], v[600];//拆分后的存储
    int dp[10005];
    int index;
    cin >> C;
    while (C--)
    {
        int i, j;
        memset(p, 0, sizeof(p));
        memset(h, 0, sizeof(h));
        memset(c, 0, sizeof(c));
        memset(dp, 0, sizeof(dp));
        memset(w, 0, sizeof(w));
        memset(v, 0, sizeof(v));
        cin >> n >> m;
        index = 1;
        for (i = 1; i <= m; i++)
        {
            cin >> p[i] >> h[i] >> c[i];
            for (j = 1; j <= c[i]; j <<= 1)
            {
                v[index] = j*p[i];
                w[index++] = j*h[i];
                c[i] -= j;
            }
            if (c[i] > 0)
            {
                v[index] = c[i] * p[i];
                w[index++] = c[i] * h[i];
            }
        }
        /*for (i = 1; i < index; i++)
            cout << v[i] << " " << w[i] << endl;*/

        for (i = 1; i < index; i++)
            for (j = n; j >= v[i]; j--)
                dp[j] = max(dp[j], dp[j - v[i]] + w[i]);

        cout << dp[n] << endl;
    }
    return 0;
}

下面我来说一下我对多重背包的理解,time to 胡言乱语。

这里写图片描述

这是将多重背包拆分后的相当于p,w的数组的结果。
行吧…我还不是很懂。

虽然不懂,但是也要记录一下自己的想法,就大概的意思就是想要把n[i]个物品拆分掉,最简单的方法就是把它拆分成n[i]个物品,相当于01背包,但是这样就是三重循环,一旦数目变大就会超时,所以就想到了二进制拆分,但是我觉得这样子拆分就像上面跑出来的程序一样,会不会出现那种不是一下子拿走系数个,而是只拿一个是最优解的情况呢,所以我决定去胡诌一个数据试试
o( ̄ヘ ̄o#)

这里写图片描述

这里写图片描述

这是打通我任督二脉的两组数据,突然一下子就懂了,按照这个方法拆分,总是可以凑出1~n[i],这之间的每一个数字,所以无论取几个成为最优解,都是不会遗漏的,动态规划的意义也就在这里!要说一下的就是上面第一组数据中的13是1and2and4and8(不可以)and6,6不是和1、2、4一起的!

这个多重背包问题的模板真的好厉害,完全融会贯通01背包和完全背包!

多重背包问题的模板

#include<iostream>  
#include<stdio.h>  
#include<algorithm>  
#include<cstring>  
#include<string.h>  
#include<string>  
using namespace std;  

int c[105],w[105],num[105];  
int dp[105];  
int v,V,V1;  

void ZeroOnePack(int c, int w)  
{  
    for(int v = V; v >=c; v--)  
    {  
        dp[v] = max(dp[v],dp[v-c]+w);  
    }  
}  

void CompletePack(int c, int w)
{  
    for(int v = c; v <= V; v++)  
    {  
        dp[v] = max(dp[v],dp[v-c]+w);  
   }  
}  

void MultiplePack(int c, int w, int num)  
{  
    if(c * num >= V)  
    {  
        CompletePack(c,w);  
    } 
    //如果总容量比这个物品的容量小,那么这个物品相当于取不完,那就是完全背包问题

    else//否则就要转化成01背包  
    {  
        int k = 1;  
        while(k < num)  
        {  
            ZeroOnePack(k*c, k*w);  
            num -= k;  
            k <<= 1;  
        }  
        ZeroOnePack(num*c, num*w);  
    }  
}  
int main()  
{  
    int t;  
    scanf("%d",&t);  
    int n;  
    int i;  
    while(t--)  
    {  
        scanf("%d%d",&V,&n);  
        for(i = 1; i <=n; i++)  
        {  
            scanf("%d%d%d",&c[i], &w[i], &num[i]);  
        }  
        memset(dp,0,sizeof(dp));  
        for(i = 1; i <= n; i++)  
        {  
           MultiplePack(c[i],w[i],num[i]);  
        }  
       printf("%d\n",dp[V]);  
    }  
    return 0;  
}  

非常强!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值