codevs 3269 混合背包(模板)

这篇博客介绍了如何解决混合背包问题,包括01背包、完全背包和多重背包的解题策略。题目描述了背包体积和物品的体积、价值与数量限制,提出了在不超过背包体积的情况下最大化物品价值的方法。博客给出了样例输入和输出,并提供了数据范围和提示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

go to the problem

PS:这应该是我有史以来写的字最少的一篇题解blog了╰( ̄▽ ̄)╭2333,发个模板留着用。

时间限制: 1 s
空间限制: 256000 KB
题目等级 : 钻石 Diamond

题目描述 Description

背包体积为V ,给出N个物品,每个物品占用体积为Vi,价值为Wi,每个物品要么至多取1件,要么至多取mi件(mi > 1) , 要么数量无限 , 在所装物品总体积不超过V的前提下所装物品的价值的和的最大值是多少?

输入描述 Input Description

第一行两个数N,V,下面N行每行三个数Vi,Wi,Mi表示每个物品的体积,价值与数量,Mi=1表示至多取一件,Mi>1表示至多取Mi件,Mi=-1表示数量无限

输出描述 Output Description

1个数Ans表示所装物品价值的最大值

样例输入 Sample Input

2 10

3 7 2

2 4 -1

样例输出 Sample Output

22

数据范围及提示 Data Size & Hint
对于100%的数据,V <= 200000 , N <= 200

i:当前处理的物品 。 j:加上当前物品后达到体积j的最大价值。
满足最优性和无后效性。

*01背包:

if(j>=v[i])     f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]) ;
else f[i][j]=f[i-1][j];  

优化一维:

for(int i=1;i<=N;++i)
  for(int j=V;j>=v[i];--j)  //!
     f[j]=max(f[j],f[j-v[i]]+w[i]);

*完全背包

1.过滤掉w[i] < w[j] && v[i] > v[j]的物品 + 二进制拆分成01背包。
2.01背包的一维优化中把j反过来枚举。

*多重背包

二进制拆分成01背包。

二进制拆分: 自己打的 ,不知道具体怎么样打(*/ω\*)。
思想:把n分解为1+2+4+8+…+k。(=x)由于可能不能恰好分解,剩下的n-x如果在前面有相同的,就把相同的-1,n-x+1.

void Done_many(int v,int w,int m)
{
    int k=1,x=0;
    memset(a,0,sizeof(a));
    while(m)
    {
        if(m-k>=0) a[++x]=k,m-=k;
        else break;
        k*=2;
    }
    if(m>1) {
        int i=1; while(a[i]!=m&&i!=x+1) ++i; if(i<=x) --a[i],++a[x+1]; a[++x]+=m;  
    } 
    if(m==1) a[++x]=m;
    for(int i=1;i<=x;++i)
       Build(v*a[i],w*a[i],1);
}

代码

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

int V,N,v,w,c,m,Cnt,cnt;
int f[200050],a[65000];
struct maple{
    int v,w,m;
}thing[100050]; 

void Build(int v,int w,int m) 
{
    thing[++cnt]=(maple){ v,w,m };
}
void Done_many(int v,int w,int m)
{
    int k=1,x=0;
    memset(a,0,sizeof(a));
    while(m)
    {
        if(m-k>=0) a[++x]=k,m-=k;
        else break;
        k*=2;
    }
    if(m>1) {
        int i=1; while(a[i]!=m&&i!=x+1) ++i; if(i<=x) --a[i],++a[x+1]; a[++x]+=m;  
    } 
    if(m==1) a[++x]=m;
    for(int i=1;i<=x;++i)
       Build(v*a[i],w*a[i],1);
}
int main()
{
    scanf("%d%d",&N,&V);
    for(int i=1;i<=N;++i)
    {
        scanf("%d%d%d",&v,&w,&m);
        if(m>1) Done_many(v,w,m);
        else Build(v,w,m);
    }
    for(int i=1;i<=cnt;++i)
    {
        if(thing[i].m==1)
           for(int j=V;j>=thing[i].v;--j)
              f[j]=max(f[j],f[j-thing[i].v]+thing[i].w);
        else 
          for(int j=thing[i].v;j<=V;++j)
              f[j]=max(f[j],f[j-thing[i].v]+thing[i].w);
    }
    cout<<f[V];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值