P1064 金明的预算方案解题报告(有依赖的背包)

题目描述

题目:https://www.luogu.com.cn/problem/P1064

思路分析

一拿到题目看着以为是01背包,但是仔细一看,发现有主件和附件之间的关系,所以不能直接用01背包。后来去查了背包9讲,发现这是个有依赖的背包。大致思路如下:首先,我们需要对每一组主附件进行01背包,找出在一定金钱(背包容量)下的最大价值重要性积的和(好绕口。。。)。

	for (register int i = 0; i < gds.size(); i++)
	{
		for (register int j = 0; j < gds[i].size(); j++)//选附件 
		{
			for (register int k = n; k >= gds[i][j].v; k--)
			{
				if (j == 0)//选主件的时候,只要放得下都放下去。
				{
					dp1[i][k] = gds[i][j].v * gds[i][j].p;
					dp2[i][k] = j;
				}
				else//选附件的时候
				{
					if (dp1[i][k - gds[i][j].v] != 0)
					//只有在k - gds[i][j].v容量下,可以选择主件的情况下,才可以选择附件。
					{
						dp1[i][k] = max(dp1[i][k], dp1[i][k - gds[i][j].v] + gds[i][j].v * gds[i][j].p);//比较,取最大。
					}
				}
			}
		}
	}

接下来,需要对所有的主附件进行01背包。注意这里的处理,这已经是我能想得到的最好的处理方法了。。。不然需要大改代码。。。

for (register int i = 0; i < gds.size(); i++)
	{
		for (register int j = n; j >=gds[i][0].v; j--)
		{
			dp[j] = max(dp[j], dp[j - gds[i][0].v] + dp1[i][gds[i][0].v]);
			//比较装下(该主件的最大值)和(不装下该主件下的最大值)的最大值(*)
			if (gds[i].size() > 1 && j >= gds[i][0].v + gds[i][1].v)
			{
				dp[j] = max(dp[j], dp[j - gds[i][1].v - gds[i][0].v] + dp1[i][gds[i][1].v + gds[i][0].v]);
				//判断装下主件+附件1的时候的最大值和之前(*)式子的结果,取最大。(**)
			}
			if (gds[i].size() > 2 && j >= gds[i][0].v + gds[i][2].v)
			{
				dp[j] = max(dp[j], dp[j - gds[i][0].v - gds[i][2].v] + dp1[i][gds[i][0].v + gds[i][2].v]);//取装下主件+附件2和之前的所有结果的最大值
			}
			if (gds[i].size() > 2 && j >= gds[i][0].v + gds[i][1].v + gds[i][2].v)
			{
				dp[j] = max(dp[j], dp[j - gds[i][0].v - gds[i][1].v - gds[i][2].v] + dp1[i][gds[i][1].v + gds[i][2].v + gds[i][0].v]);
				//取装下主件+附件1+附件2与之前所有结果的最大值
			}
		}
	}

剩下的,和普通背包一样,输出n容量下的最大值即可(好难。。。)

完整代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <cstring>
#include <cstdlib>
using namespace std;
struct good {
	int v;
	int p;
};
map<int, int> re;
vector<vector<good>> gds;
long dp1[70][30005] = { 0 };
long dp2[70][30005] = { 0 };
long dp[32005] = { 0 };

int main()
{
	int n, m;
	cin >> n >> m;
	int dis = 0;
	int key = 0;
	for (int i = 0; i < m; i++)
	{
		vector<good> tmp;
		good t;
		cin >> t.v >> t.p;
		int j;
		cin >> j;
		if (j == 0)
		{
			re[i + 1] = key++;
			tmp.push_back(t);
			gds.push_back(tmp);
		}
		else
		{
			gds[re[j]].push_back(t);
			dis++;
		}
	}
	memset(dp1, 0, sizeof(dp1));
	for (register int i = 0; i < gds.size(); i++)
	{
		for (register int j = 0; j < gds[i].size(); j++)//选附件 
		{
			for (register int k = n; k >= gds[i][j].v; k--)
			{
				if (j == 0)
				{
					dp1[i][k] = gds[i][j].v * gds[i][j].p;
					dp2[i][k] = j;
				}
				else
				{
					if (dp1[i][k - gds[i][j].v] != 0)
					{
						dp1[i][k] = max(dp1[i][k], dp1[i][k - gds[i][j].v] + gds[i][j].v * gds[i][j].p);
					}
				}
			}
		}
	}
	for (register int i = 0; i < gds.size(); i++)
	{
		for (register int j = n; j >=gds[i][0].v; j--)
		{
			dp[j] = max(dp[j], dp[j - gds[i][0].v] + dp1[i][gds[i][0].v]);
			if (gds[i].size() > 1 && j >= gds[i][0].v + gds[i][1].v)
			{
				dp[j] = max(dp[j], dp[j - gds[i][1].v - gds[i][0].v] + dp1[i][gds[i][1].v + gds[i][0].v]);
			}
			if (gds[i].size() > 2 && j >= gds[i][0].v + gds[i][2].v)
			{
				dp[j] = max(dp[j], dp[j - gds[i][0].v - gds[i][2].v] + dp1[i][gds[i][0].v + gds[i][2].v]);
			}
			if (gds[i].size() > 2 && j >= gds[i][0].v + gds[i][1].v + gds[i][2].v)
			{
				dp[j] = max(dp[j], dp[j - gds[i][0].v - gds[i][1].v - gds[i][2].v] + dp1[i][gds[i][1].v + gds[i][2].v + gds[i][0].v]);
			}
			//cout << dp[j] << " ";
		}
		//cout << endl;
	}
	cout << dp[n];
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值