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(nW∑mi)
,一般题目承受不住,所以需要优化方法。
- 方法二:二进制优化,将mi个物品用二进制划分,即
mi=21+22+…+2x+reminder
这样就把m_i个物品分割成了几个重量和价值都为原来的
2x倍的物品
,最后多出来的部分也看成一个物品,所以复杂度为
O(nW∑log2mi)
- 方法三:单调队列优化,可以将复杂度优化到 O(nW) ,状态转移方程为
dp[i+1][j]=max{dp[i][j−k×w[i]]+k×v[i]|0≤k≤mi}
这样的复杂度和方案一是一样的,所以我们可以考虑不同的 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]+(k−1)×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;
}