集训 背包问题

1.01背包

山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。

最基础的一个背包,首先考虑状态转移,设dp[i][j]表示容量为j,放入前i个物品所得的最大价值

很明显,如果还有剩余的话,dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])

然后考虑滚动数组压掉一维

很明显,当前状态只能从前一个状态转来,所以可以去掉i这一维。

dp[j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])

#include<bits/stdc++.h>
using namespace std;
int t,m;
int w[100010],v[100010],f[100010];
int main(){
	scanf("%d%d",&t,&m);
	for(int i=1 ; i<=m ; i++) scanf("%d%d",&w[i],&v[i]);
	for(int i=1 ; i<=m ; i++){
		for(int j=t ; j>=w[i] ; j--){ //注意倒着循环
			f[j] = max(f[j],f[j-w[i]]+v[i]);
		}
	}
	printf("%d",f[t]);
	return 0;
}

2.完全背包

山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。 

此题和原题的不同点:

1. 每种草药可以无限制地疯狂采摘。

2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

很明显, 跟01背包不同的是,每种药有无数个

状态转移方程不变,只不过是j正着循环

#include<bits/stdc++.h>
using namespace std;
int t,m;
long long w[10000010],v[10000010],f[10000010];
int main(){
	scanf("%d%d",&t,&m);
	for(int i=1 ; i<=m ; i++) scanf("%lld%lld",&w[i],&v[i]);
	for(int i=1 ; i<=m ; i++){
		for(int j=w[i] ; j<=t ; j++){//正着循环
			f[j] = max(f[j],f[j-w[i]]+v[i]);
		}
	}
	printf("%lld",f[t]);
	return 0;
}

3.多重背包

小 FF 有一个最大载重为 W 的采集车,洞穴里总共有 n 种宝物,每种宝物的价值为 vi​,重量为 wi​,每种宝物有 mi​ 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。 

 跟上面两种区别是每一个物品的数量不固定

然后考虑二进制,2^0+2^1+······+2^k ,能表示2^k+1-1的所有数

所以把物品根据二进制,分成重量为1,2,4,8······,把复杂度优化一个log

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
	return x*f;
}
int n,x;
int sum,ans;
int dp[1000010];
int V[1000010],W[1000010];
int main(){
	n=read();x=read();
	for(register int i(1) ; i<=n ; i=-~i){
		int v,w,m;
		v=read();w=read();m=read();
		for(register int j(1) ; j<=m ; j*=2){ //类似二进制
			sum++;
			V[sum] = v*j; 
			W[sum] = w*j;  
			m-=j; 
		}
		sum++;
		V[sum] = v*m;
		W[sum] = w*m;
	}
	for(register int i(1) ; i<=sum ; i=-~i){
		for(register int j(x) ; j>=W[i] ; --j){ //相当于01背包
			dp[j] = max(dp[j],dp[j-W[i]]+V[i]);
		}
	}
	printf("%d",dp[x]);
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值