洛谷题单指南-动态规划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;
}

 

posted @ 2024-04-22 15:08  五月江城  阅读(28)  评论(0编辑  收藏  举报