洛谷P1064 金明的预算方案(分组背包)

其实一开始并不是用分组背包AC的(当是还没学),而是01背包加分类讨论过的,但写的很复杂,觉得应该有更简单的算法,查了才知道可以用分组背包的。

分组就是将主件与附件看成是同一组内的,主件与其他主件为不同组。同一组内有4种方案,取主件,取主件+附件1,取主件+附件2,取主件+附件3,这几个方案都是互斥的,符合分组背包的特点。

分组背包的AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=32000+5;
vector<ll> v[61],val[61];
ll dp[maxn];
bool is_main[61]={0};

int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		ll a,b,c;
		cin>>a>>b>>c;
		if(!c)
		{
			v[i].push_back(a);
			val[i].push_back(a*b);
			is_main[i]=1;
		}
		else
		{
			int size=v[c].size();
			for(int i=0;i<size;i++)
			{
				ll temp=v[c][i];
				v[c].push_back(a+temp);
			}
			for(int i=0;i<size;i++)
			{
				ll temp=val[c][i];
				val[c].push_back(a*b+temp);
			}
		}
	}
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=m;i++)		//分组背包标准模板
	{
		if(!is_main[i])	continue;
		for(int j=n;j>=0;j--)
		{
			for(int k=0;k<v[i].size();k++)
			{
				if(j>=v[i][k])
					dp[j]=max(dp[j],dp[j-v[i][k]]+val[i][k]); 
			}
		}
	}
	ll ans=0;
	cout<<dp[n]<<endl;
	return 0;
}

分类讨论需要设置结构体完成DP,在下面的代码中会有解释。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=32000+5;
struct subject
{
	ll sum;		//记录最大收益
	bool check[61];	//check[i]=1表示i物品有购买,0表示没购买
	subject()
	{
		sum=0;
		memset(check,0,sizeof(check));
	}
}dp[maxn];
ll v[61],val[61];
int fa[61];

int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		ll a,b,c;
		cin>>a>>b>>c;
		v[i]=a; val[i]=a*b; fa[i]=c;
	}
	for(int i=1;i<=m;i++)
		for(int j=n;j>=v[i];j--)
		{
			if(!fa[i])		//如果是主件,就正常的01背包就好
			{
				if(dp[j].sum<dp[j-v[i]].sum+val[i])
				{
					dp[j]=dp[j-v[i]];
					dp[j].sum+=val[i];
					dp[j].check[i]=1;		//i被购买了
				}
			}	
			else	//如果是附件
			{
				if(dp[j-v[i]].check[fa[i]])		//如果j-v[i]的方案中,有购买fa[i]
				{
					if(dp[j].sum<dp[j-v[i]].sum+val[i])		
					{
						dp[j]=dp[j-v[i]];
						dp[j].sum+=val[i];
						dp[j].check[i]=1;
					}
				}
				else if(j>=v[i]+v[fa[i]])	//如果j-v[i]的方案中没有fa[i],就把i与fa[i]看成一个整体,
				//判断是否购买这个整体会不会更优,前提是钱够(j>=v[i]+v[fa[i]])
				{
					if(dp[j].sum<dp[j-v[i]-v[fa[i]]].sum+val[i]+val[fa[i]])
					{
						dp[j]=dp[j-v[i]-v[fa[i]]];
						dp[j].sum+=val[i]+val[fa[i]];
						dp[j].check[i]=dp[j].check[fa[i]]=1;
					}
				}
			}
		}
	cout<<dp[n].sum<<endl;
	return 0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值