背包

01 背包

\(n\) 件物品,每件物品有权值和重量,给出背包体积 \(V\),从这些物品中挑选若干件(只能选一次)放入背包,使得背包内物品的总重量不超过 \(V\),问能可以得到的最大权值。

\(dp[i][j]\) 选取前 \(i\) 件物品重量为 \(j\) 能取得的最大的权值,可以得到转移方程 \(dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);\)

因为 i 只与 i-1 有关所以考虑滚动数组:

交替滚动

用 now 表示现在的数组,用 old 表示上一个数组,交替使用。

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[3][10010];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>w[i]>>val[i];
	}
	int now=0,old=1;
	for(int i=1;i<=m;i++){
		swap(old,now);
		for(int j=0;j<=t;j++){
			if(j>=w[i]){
				dp[now][j]=max(dp[old][j],dp[old][j-w[i]]+val[i]);
			}
			else{
				dp[now][j]=dp[old][j];
			}
		}
	}
	cout<<dp[now][t];
    return 0;
}

自我滚动

因为体积只会向小减少,所以从后向前遍历容量以确保每个数物品只选一次,上一次的数组储存的数(i-1) 结果全部就在这个数组里,这样空间压缩到一维。

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[10010];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>w[i]>>val[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=t;j>=w[i];j--){
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

完全背包

\(n\) 件物品,每件物品有权值和重量,给出背包体积 \(V\),从这些物品中挑选若干件(可以多次同选一个)放入背包,使得背包内物品的总重量不超过 \(V\),问能可以得到的最大权值。

我们对 01 背包的代码进行更改为从小到大枚举容量,因为在计算某个容量时,可以确保之前计算的状态已经包含了该物品之前选择的次数,这样就能够正确地累积多个相同物品的价值。

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int t,m;
int w[202],val[202];
int dp[10010];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>w[i]>>val[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=w[i];j<=t;j++){
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

多重背包

\(n\) 件物品,每件物品有权值和重量,给出背包体积 \(V\),从这些物品中挑选若干件(给出共有几个)放入背包,使得背包内物品的总重量不超过 \(V\),问能可以得到的最大权值。

考虑将每个物品进行二进制拆分,拆完后就转化为 01 背包问题。(因为是二进制,所以可以保证可以取到任意组合)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int t,m;

int w[N],val[N];
long long dp[N];
int main(){
	ios::sync_with_stdio(false);
	cin>>m>>t;
	int a,b,c;
	int cnt=0;
	for(int i=1;i<=m;i++){
		cin>>a>>b>>c;
		for(int j=1;j<=c;j<<=1){//二进制 
			w[++cnt]=j*a;//第cnt个背包的重量 
			val[cnt]=j*b;//计算权值 
			c-=j;//计算剩余 
		}
		if(c){//剩余单独储存
			w[++cnt]=c*a;
			val[cnt]=c*b;
		}
	}
	for(int i=1;i<=cnt;++i){//01背包选择 
		for(int j=t;j>=w[i];--j){
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

分组背包

01背包规则,他的物品大致可分为 \(k\) 组,每组中的物品相互冲突,现在,问最大的价值是多少。

只是加入了组的限制,只需按组储存,然后 01 背包。

#include<bits/stdc++.h>
using namespace std;
int n,m;
int w[10001];
int val[10001];
int b[100001];
int g[205][205];
int dp[100001];
int main(){
	ios::sync_with_stdio(false);
	cin>>m>>n;
	int t=0;
	for(int i=1;i<=n;i++){
		int x;//组 
		cin>>w[i]>>val[i]>>x;
		t=max(t,x);//最大组 
		b[x]++;//小组物品增加 
		g[x][b[x]]=i;//第 x 组第 b[x] 个物品的编号 
	}
	for(int i=1;i<=t;i++){//枚举组数 
		for(int j=m;j>=0;j--){//枚举容量 
			for(int k=1;k<=b[i];k++){//枚举第i组的物品 
				if(j>=w[g[i][k]]){
					dp[j]=max(dp[j],dp[j-w[g[i][k]]]+val[g[i][k]]);
				}
			}
		}
	}
	cout<<dp[m];
    return 0;
}

P5322 [BJOI2019] 排兵布阵

将一个城堡看作一组
先对每组城堡中的各个玩家的敌人数进行排序
每个玩家派兵的数量*2+1 可以看作为物品重量(注意玩家派兵数量可能相同)
那么 排序后该玩家敌人数的索引与城堡索引的乘积 就是物品价值 且每个组内的物品只能选择一次

#include<bits/stdc++.h>
#define ll long long
#define int ll
#define ls t[p].l
#define rs t[p].r
#define re register 
#define pb push_back
#define pir pair<int,int> 
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define lowbit(x) x&-x;
using namespace std;
const int N=1e6+10;
const int M=2e5+4;
const int mod=1e9+7;
const int INF=1e17+7;
mt19937 rnd(251);
int n,q,m;

int a[105][105];

int f[N];

void solve(){
	cin>>n>>q>>m;
	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=q;j++){
			cin>>a[j][i];
		}
	} 
	
	for(int i=1;i<=q;i++){
		sort(a[i]+1,a[i]+1+n);
	}
	
	for(int i=1;i<=q;i++){
		for(int j=m;j>=0;j--){
			for(int k=1;k<=n;k++){
				if(j>=a[i][k]*2+1){
					f[j]=max(f[j],f[j-a[i][k]*2-1]+i*k);
				}
			}
		}
	}
	cout<<f[m];
}
signed main(){
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(nullptr);   
    int t=1;
//    cin>>t;
    while(t--){
        solve();
    }    
    return 0;
}

混合背包

背包中的物品有的可以选一次(0/1 背包),有的可以选多次(完全背包),有的数量有限(多重背包)。

无限个并不是真的无限,只需转化为最大容积除物品中重量的个数就行,然后问题就转化为多重背包了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int t,m;

int w[N],val[N];
long long dp[N];

int main(){
	ios::sync_with_stdio(false);
	cin>>t>>m;
	int a,b,c;
	int cnt=0;
	for(int i=1;i<=m;i++){
		cin>>a>>b>>c;//重,价,数	
		if(c==0){//无限个(最大容积除重量个) 
			c=t/a; 
		} 
		for(int j=1;j<=c;j<<=1){//剩下多重背包 
			w[++cnt]=j*a;
			val[cnt]=j*b;
			c-=j;
		}
		if(c){
			w[++cnt]=c*a;
			val[cnt]=c*b;
		}
	}
	for(int i=1;i<=cnt;++i){
		for(int j=t;j>=w[i];--j){//01背包 
			dp[j]=max(dp[j],dp[j-w[i]]+val[i]);
		}
	}
	cout<<dp[t];
    return 0;
}

P1779 魔鬼杀手

背包的用法

#include<bits/stdc++.h>
#define ll long long
#define int ll
#define ls t[p].l
#define rs t[p].r
#define re register 
#define pb push_back
#define pir pair<int,int> 
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define lowbit(x) x&-x;
using namespace std;
const int N=3e6+10;
const int M=2e6+5;
const int mod=1e9+7;
const int INF=1e17+7;
mt19937 rnd(251);

int n,m; 
int a[N];

int bv[N],cv[N];
int bt[N],ct[N];
int cnt1=0,cnt2=0;
int f1[N],f2[N];

int check(int x){
	int sum=0;
	for(int i=1;i<=n;i++){
		if(a[i]-x>0){
			sum+=f2[a[i]-x];
		}
	}
	return sum;	
}

void solve(){
	
	for(int i=1;i<=M;i++){
		f1[i]=f2[i]=INF;
	}
	
	cin>>n;
	
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	
	cin>>m;
	
	for(int i=1;i<=m;i++){
		string sd,sf;
		int x,y;
		cin>>sd>>x>>sf>>y;
		
		if(x==0&&y!=0){
			cout<<0;
			return;
		} 
		
		if(sf=="All"){
			cnt1++;
			bt[cnt1]=min(y,M);	
			bv[cnt1]=x;	
		}
		else{
			cnt2++;
			ct[cnt2]=min(y,M);	
			cv[cnt2]=x;	
		}
	}
	
	for(int i=1;i<=cnt1;i++){
		for(int j=bt[i];j<=M;j++){
			f1[j]=min(f1[j],f1[j-bt[i]]+bv[i]);
		}
	}
	
	for(int i=1;i<=cnt2;i++){
		for(int j=ct[i];j<=M;j++){
			f2[j]=min(f2[j],f2[j-ct[i]]+cv[i]);
		}
	}
	
	for(int j=M-1;j>=0;j--){
		f2[j]=min(f2[j],f2[j+1]);
	}
	
	int ans=INF;
	for(int j=0;j<=M;j++){
		ans=min(ans,f1[j]+check(j));
	}
	
	cout<<ans;
}
signed main(){
//    freopen("a.in","r",stdin);
//    freopen("a.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(nullptr);   
    int t=1;
//    cin>>t;
    while(t--){
        solve();
    }    
    return 0;
}
posted @ 2024-09-05 21:35  sad_lin  阅读(28)  评论(0)    收藏  举报