洛谷题单指南-动态规划1-P1064 [NOIP2006 提高组] 金明的预算方案
原题链接:https://www.luogu.com.cn/problem/P1064
题意解读:用固定钱数购买最大价值的物品。
解题思路:
背包问题,背包问题里的体积相当于物品价格,价值相当于价格 * 重要度
物品分为主件、附件,主件最多有0/1/2个附件,要选附件必须选相应主件,
因此在递推计算dp[j]总价格j能购买的最大价值时,对于第i个主件,可以有5种可能:
1、不买第i个主件任何物品
2、只买第i个主件
3、买第i个主件和他的第1个附件(如果有)
4、买第i个主件和他的第2个附件(如果有)
5、买第i个主件和他的1,2两个附件(如果有)
求最大值即可,直接用此方式进行递推求解也没问题,但本题用另外一种更接近标准模版的方法。
既然以上5种情况每次只选一种,那么该问题可以转化为分组背包问题:
每个主件就是一个分组,根据主件的附件数量不同,分组里有不同的物品:
1、如果一个主件有0个附件,则该组只有一个物品,对应主件的体积和价值
2、如果一个主件有1个附件,则该组有两个物品
- 物品1:主件的体积和价值
- 物品2:主件+附件的体积和价值
3、 如果一个主件有2个附件,则该组有四个物品,
- 物品1:主件的体积和价值
- 物品2:主件+附件1的体积和价值
- 物品3:主件+附件2的体积和价值
- 物品4:主件+物品1+物品2的体积和价值
4、所有分组都有一个体积、价值为0的物品,表示不选该分组任何物品
将物品分组之后,就可以用分组背包的模版代码实现DP过程。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 65, V = 32005;
struct node
{
int v; //体积 : 对应价格
int w; //价值 : 对应价格 * 重要度
};
int no[N]; //所有主件的物品编号
int cnt; //主件数量
node a[N]; //主件
vector<node> b[N]; //主件对应的附件
vector<node> g[N]; //g[i]表示第i组的所有物品
int dp[V]; //dp[j]表示总价格是j的最大价值
int n, m, v, p, q;
int main()
{
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
cin >> v >> p >> q;
if(q == 0) //存主件
{
no[++cnt] = i;
a[i].v = v;
a[i].w = v * p;
}
else b[q].push_back({v, v * p}); //存主件对应的附件
}
/*
将主件和对应的附件处理成分组
每个主件对应一个组,
如果一个主件有0个附件,则该组只有一个物品,对应主件的体积和价值
如果一个主件有1个附件,则该组有两个物品,物品1:主件的体积和价值,物品2:主件+附件的体积和价值
如果一个主件有2个附件,则该组有四个物品,物品1:主件的体积和价值,物品2:主件+附件1的体积和价值,物品3:主件+附件2的体积和价值,物品4:主件+物品1+物品2的体积和价值
所有分组都有一个体积、价值为0的物品,表示不选该分组任何物品
*/
for(int i = 1; i <= cnt; i++)
{
int idx = no[i];
g[idx].push_back({0, 0}); //每个分组可以不选,分组第0个物品体积和价值都是0表示不选该组
g[idx].push_back({a[idx].v, a[idx].w});
if(b[idx].size() >= 1)
{
g[idx].push_back({a[idx].v + b[idx][0].v, a[idx].w + b[idx][0].w});
}
if(b[idx].size() >= 2)
{
g[idx].push_back({a[idx].v + b[idx][1].v, a[idx].w + b[idx][1].w});
g[idx].push_back({a[idx].v + b[idx][0].v + b[idx][1].v, a[idx].w + b[idx][0].w + b[idx][1].w});
}
}
//下面采用分组背包的算法
for(int i = 1; i <= cnt; i++)
{
int idx = no[i]; //每个分组的编号
for(int j = n; j >= 0; j--)
{
for(int k = 0; k < g[idx].size(); k++)
{
if(j >= g[idx][k].v) dp[j] = max(dp[j], dp[j - g[idx][k].v] + g[idx][k].w);
}
}
}
cout << dp[n];
return 0;
}