P1064 金明的预算方案

[题目](https://www.luogu.org/problem/P1064)

这是一道不错的动态01背包的题目。
说起来我被背包问题整得很惨,在niop 2018 的时候就出了一道完全背包计数的题目货币系统,
可是那时,我才初步走上OI的道路,行程开始才五个月。水平比较低下。
但最气人的是我那时候不知怎么就是知道背包可以解决这类问题,就是不会写,day 1 T2 直接爆0。
唉,往事不堪回首。
现在进入正题。
这道题目是一道最优性问题,
最优性问题呢,据我了解,有两种方法

贪心 和 DP

而这道题目贪心显然行不通。
而且一看也知道这题是一个01背包。
那么废话了一堆,DP的转移方程怎么设计。

01背包最基本的方程

if(f[i]<f[i-v[i]]+w[i])
	f[i]=f[i-v[i]]+w[i];

最麻烦的东西其实还是主件和附件的处理,
原则
1 有主件不一定有附件。
2有附件一定有主件。
那么新的方程就来了

if(第i件是主件&&f[i]<f[i-v[i]]+w[i])
	f[i]=f[i-v[i]]+w[i];
if(第i件是附件&&在f[i-v[i]]的方案选择中有i的主件){
	if(f[i]<f[i-v[i]]+w[i])
	f[i]=f[i-v[i]]+w[i];
}
设i的主件的编号是x
if(第i件是附件&&在f[i-v[i]]的方案选择中没有i的主件){
		int s=i-v[i]-v[x];
	if(s>=0&&在f[s]的选择方案中没有物品i和x){
			if(f[i]<f[s]+w[x]+w[i])
				f[i]=f[x]+w[x]+w[i];
	}
}

虽然方程出来了,但是还有一个问题没有解决;
就是如何判断f[i]的方案选择
由于本题较水,多开一维就搞定了。
AC代码如下

#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[32005][65],v[65],w[65],pd[65];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int k;
		scanf("%d%d%d",&v[i],&k,&pd[i]);
		w[i]=v[i]*k;
	}
	for(int i=1;i<=m;i++)
		for(int j=n;j>=v[i];j--){
			int p=pd[i];//主附件
			if(f[j][i]) continue;
			if(p==0&&f[j][0]<f[j-v[i]][0]+w[i]){
				f[j][0]=f[j-v[i]][0]+w[i];
				for(int k=1;k<=m;k++)
					f[j][k]=f[j-v[i]][k];
					f[j][i]=1;
			}
			if(p&&f[j-v[i]][p]&&f[j][0]<f[j-v[i]][0]+w[i]){
				f[j][0]=f[j-v[i]][0]+w[i];
				for(int k=1;k<=m;k++)
					f[j][k]=f[j-v[i]][k];
					f[j][i]=1;
			}
			int h=j-v[i]-v[p];
			if(p&&h>=0&&!f[h][p]&&!f[h][i]){
				if(f[j][0]<f[h][0]+w[i]+w[p]){
					f[j][0]=f[h][0]+w[i]+w[p];
					for(int k=1;k<=m;k++)
						f[j][k]=f[h][k];
					f[j][i]=1; f[j][p]=1;
				}
			}
		}
cout<<f[n][0];	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值