动态规划背包问题(洛谷,01背包和完全背包)

ACM学习汇总

P1060 开心的金明
有N元,m件商品,买第i件商品得到的值 t[i] = val[i]*wei[i]
求所买商品的t[i]总和最大值
-----------------------------------------------------------------------------
01背包裸题
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; i++)
using namespace std;
const int maxn = 30005;
int n,m,v[maxn],w[maxn],f[maxn];
void get_zero_one(){
	fo(i,1,m){ // 物品 
		for(int j=n; j>0; j--){ //钱 
			if(j-v[i]>=0)f[j] = max(f[j], f[j-v[i]]+v[i]*w[i]);
		}
	}
	cout<<f[n];
}
int main(){
	scanf("%d%d",&n,&m);
	fo(i,1,m)scanf("%d%d",&v[i],&w[i]); 
	get_zero_one();
	return 0;
}
P1164 小A点菜
N元,m件商品,每件价格为a[i],问刚好买完N元的方案数
-------------------------------------------------
f[0]=1,直接01背包
#include<bits/stdc++.h>
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; i++)

using namespace std;
int n,m,a[105];
int f[10005];
void zero_one_back(){
	// f[0] = 1 是最原始的情况,在每次枚举一件商品的时候,都能保证它是在最后进行计算的,保证了一件商品不会被重复选择 
	f[0] = 1; // 刚好买完。本来的子结构是,f[a[i]]=1的。但是不方便同一代码 
	fo(i,1,n){ // 商品 
		for(int j=m; j>=a[i]; j--){ // 往前选,保证了前面的一定没选过这道菜 
			f[j] += f[j-a[i]]; // 没选加上选择 = 总选法数 
		}
	}
//	fo(i,1,n){
//		for(int j=m; j>a[i]; j--){
//			f[j] += f[j-a[i]]; // 
//		}
//		f[a[i]]+=1; // 价格是从大到小计算,有或没有a[i]的情况的总数,为了保证不重复,刚好选择的这种情况要放最后 
//	}
	cout<<f[m];
}
int main(){
	scanf("%d%d",&n,&m);
	fo(i,1,n)scanf("%d",&a[i]);
	zero_one_back(); 
	return 0;
}
P1064 金明的预算方案
N元,m件商品,求价格*权重和的最大值
商品有主从件之分,要先买了主件才能买从件
----------------------------------------
01背包,在枚举商品的时候判断是否为主件 
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
struct node{
	int v, p, q;
	struct node *son1=NULL,*son2=NULL;
}a[70];
int n,m,f[40000];
void solve(){
	fo(i,1,m){
		if(a[i].q==0){
			for(int j=n; j>=a[i].v; j--){
				f[j] = max(f[j], f[j-a[i].v]+a[i].v*a[i].p);
				if(a[i].son1!=NULL){
					if(j-a[i].v-a[i].son1->v>=0) // 结构体指针要用 -> 访问他指向的内存 
					f[j] = max(f[j], f[j-a[i].v-a[i].son1->v]+a[i].v*a[i].p+a[i].son1->v*a[i].son1->p);
				}
				if(a[i].son2!=NULL){
					if(j-a[i].v-a[i].son2->v>=0)
					f[j] = max(f[j], f[j-a[i].v-a[i].son2->v]+a[i].v*a[i].p+a[i].son2->v*a[i].son2->p);
				}
				if(a[i].son1!=NULL && a[i].son2!=NULL){
					if(j-a[i].v-a[i].son1->v-a[i].son2->v>=0)
					f[j] = max(f[j], f[j-a[i].v-a[i].son1->v-a[i].son2->v]+a[i].v*a[i].p+a[i].son1->v*a[i].son1->p+a[i].son2->v*a[i].son2->p);
				}
			} 
		}
	}
	cout<<f[n];
}
int main(){
	cin>>n>>m;
	fo(i,1,m){
		scanf("%d%d%d",&a[i].v,&a[i].p,&a[i].q);
		if(a[i].q){
			if(a[a[i].q].son1==NULL)a[a[i].q].son1 = &a[i];
			else a[a[i].q].son2 = &a[i];
		}
	}
	solve();
	return 0;
}
P1048 采药
时间N,m个草药,采药时间花费为w[i],价值为v[i],求采药总价值最大
------------------------------------------------------------
直接01背包
#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int f[1005],v[105],w[105],n,m;
void zero_one_pack(){
	fo(i,1,m){
		for(int j=n; j>=v[i]; j--){
			f[j] = max(f[j], f[j-v[i]]+w[i]);
		}
	}
	cout<<f[n];
}
int main(){
	cin>>n>>m;
	fo(i,1,m){
		scanf("%d%d",&v[i],&w[i]);
	}
	zero_one_pack();
	return 0;
}
P1049 装箱问题
箱子体积V,m件物品体积v[i],问怎么装才使得剩余体积最小,输出最小剩余体积
--------------------------------------------------------------------
f[n]表示n体积最多放的体积为f[n]
01背包后输出n-f[n]
#include<bits/stdc++.h>
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,m,f[20005],v[40];
void solve(){
	fo(i,1,m){
		for(int j=n; j>=v[i]; --j){
			f[j] = max(f[j], f[j-v[i]]+v[i]);
		}
	}
	cout<<n-f[n];
}
int main(){
	cin>>n>>m;	
	fo(i,1,m)scanf("%d",&v[i]);
	solve();
	return 0;
}
P1616 疯狂的采药
时间N,m种药,采药时间t[i],价值v[i],每株药无限,求最大采药价值
-----------------------------------------------------------
完全背包
#include<bits/stdc++.h>
#define ll long long 
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int n,m,f[100005],v[10005],w[10005];
void solve(){
	fo(i,1,m){
		for(int j=v[i]; j<=n; j++){ // 完全背包 
			f[j] = max(f[j], f[j-v[i]]+w[i]);  
		}
	}
	cout<<f[n];
}
int main(){
	cin>>n>>m;
	fo(i,1,m)scanf("%d%d",&v[i],&w[i]);
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值