51nod 1086 背包问题 V2(多重背包问题)

1086 背包问题 V2

基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注
有N种物品,每种物品的数量为C1,C2……Cn。从中任选若干件放在容量为W的背包里,每种物品的体积为W1,W2……Wn(Wi为整数),与之相对应的价值为P1,P2……Pn(Pi为整数)。求背包能够容纳的最大价值。
Input
第1行,2个整数,N和W中间用空格隔开。N为物品的种类,W为背包的容量。(1 <= N <= 100,1 <= W <= 50000)
第2 - N + 1行,每行3个整数,Wi,Pi和Ci分别是物品体积、价值和数量。(1 <= Wi, Pi <= 10000, 1 <= Ci <= 200)
Output
输出可以容纳的最大价值。
Input示例
3 6
2 2 5
3 3 8
1 4 1
Output示例
9


这题是经典的多重背包问题
多重背包和01背包还有完全背包不同,对每个物品的个数有限制mi个
一般有三种解决方法:
- 方法一:普通dp方法的话,先枚举n个物品,然后枚举W的容量,然后枚举 mi 个东西,总复杂度是 O(nWmi) ,一般题目承受不住,所以需要优化方法。
- 方法二:二进制优化,将mi个物品用二进制划分,即

mi=21+22++2x+reminder
这样就把m_i个物品分割成了几个重量和价值都为原来的 2x ,最后多出来的部分也看成一个物品,所以复杂度为 O(nWlog2mi)
- 方法三:单调队列优化,可以将复杂度优化到 O(nW) ,状态转移方程为
dp[i+1][j]=max{dp[i][jk×w[i]]+k×v[i]|0kmi}
这样的复杂度和方案一是一样的,所以我们可以考虑不同的 j%w[i] 值,因为不同的值之间是相互独立的,所以可以得到枚举余数r,定义
a[j]=dp[i][j×w[i]+r]
转移方程可以写为
dp[i+1][(j+k)×w[i]+r]=max{a[j]+k×v[i],a[j+1]+(k1)×v[i],,a[j+k]}
这样还是不能很方便的计算,所以再次进行变形
b[j]=a[j]j×v[i]
dp[i+1][(j+k)×w[i]+r]=max{b[j],b[j+1],,b[j+k]}+(j+k)×v[i]
这样就可以用单调队列维护长度最多为 mi 的区间的最大值了。


下面是二进制优化方法的代码

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           5005
#define   MAXN          1000005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

int p[MAX];
int w[MAX];
int dp[50005];
int main(){
    //freopen("in.txt","r",stdin);
    int n,W;
    cin>>n>>W;
    int tot=0;
    for(int i=0;i<n;i++){
        int wi,pi,ci;
        scanf("%d%d%d",&wi,&pi,&ci);
        int ret=1;
        while(ci>=ret){
            p[tot]=pi*ret;
            w[tot++]=wi*ret;
            ci-=ret;
            ret*=2;
        }
        if(ci!=0){
            p[tot]=ci*pi;
            w[tot++]=wi*ci;
        }
    }
    mem(dp,-1);dp[0]=0;
    int maxn=0;
    for(int i=0;i<tot;i++){
        for(int j=W;j>=w[i];j--){
            if(dp[j-w[i]]!=-1) dp[j]=max(dp[j],dp[j-w[i]]+p[i]);
            maxn=max(maxn,dp[j]);
        }
    }
    cout<<maxn<<endl;
    return 0;
}

这是单调队列优化的代码

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")

using namespace std;
#define   MAX           5005
#define   MAXN          50005
#define   maxnode       15
#define   sigma_size    30
#define   lson          l,m,rt<<1
#define   rson          m+1,r,rt<<1|1
#define   lrt           rt<<1
#define   rrt           rt<<1|1
#define   middle        int m=(r+l)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   pii           pair<int,int>
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   limit         10000

//const int    prime = 999983;
const int    INF   = 0x3f3f3f3f;
const LL     INFF  = 0x3f3f;
const double pi    = acos(-1.0);
const double inf   = 1e18;
const double eps   = 1e-8;
const LL    mod    = 1e9+7;
const ull    mx    = 133333331;

/*****************************************************/
inline void RI(int &x) {
      char c;
      while((c=getchar())<'0' || c>'9');
      x=c-'0';
      while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
 }
/*****************************************************/

int v[MAX];
int w[MAX];
int c[MAX];//数量
int dp[MAXN];
int deq[MAXN];//保存数组下标
int deqv[MAXN];//保存值

int main(){
    //freopen("in.txt","r",stdin);
    int n,W;
    cin>>n>>W;
    int maxn=0;
    for(int i=0;i<n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
    for(int i=0;i<n;i++){
        for(int a=0;a<w[i];a++){
            int s=0,t=0;
            for(int j=0;j*w[i]+a<=W;j++){
                int tmp=dp[j*w[i]+a]-j*v[i];
                while(s<t&&deqv[t-1]<=tmp) t--;
                deq[t]=j;
                deqv[t++]=tmp;
                dp[j*w[i]+a]=deqv[s]+j*v[i];
                maxn=max(maxn,dp[j*w[i]+a]);
                if(deq[s]==j-c[i]) s++;//deq[s]内的值和j已经差了c[i]个
            }
        }
    }
    cout<<maxn<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值