洛谷试炼场 动态规划专练

// 最近才发现洛谷也有整理好的分类题集,于是前来加强练习一番。
普及练习场


在普及练习场分分钟解决了动态规划的背包问题,最后卡在 -->

P1064 金明的预算方案

重学了一遍分组背包终于解决了该问题。 参考背包问题九讲
注意分组后不能重复选啊!!!
 

解题思路

将附件做01背包求得在不同金钱下购买若干附件获得的最大价值,将所有方案加入到主件。现在新的主件中只能选择购买其中一种,于是转化为分组背包问题。
 

AC代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;

struct Node {
    int v, p;   // 花费 与 价值
};
vector<Node> Main[64];
vector<Node> Part[64];

int dp[33000];
int main() {
    int V, m;
    cin>>V>>m;
    for(int i=1;i<=m;i++) {
        int v, p, q;
        scanf("%d %d %d", &v, &p, &q);
        p *= v;
        if(!q) Main[i].push_back((Node){v, p});
        else
            Part[q].push_back((Node){v, p});
    }
    for(int i=1;i<=m;i++) {
        if(Part[i].size()==0) continue;

        // i主件的j附件 01背包
        memset(dp, 0, sizeof(dp));
        for(int j=0;j<Part[i].size();j++) {
            int v = Part[i][j].v, p = Part[i][j].p;
            for(int k=V;k>=v;k--) {
                dp[k] = max(dp[k], dp[k-v]+p);
            }
        }
        // 金钱j+Main[i][0].v 最多获得 dp[j]+Main[i][0].p
        // 全部放入Main分组
        int last = 0;
        for(int j=1;j+Main[i][0].v<=V;j++) {
            if(dp[j]>last)
                Main[i].push_back((Node){j+Main[i][0].v, dp[j]+Main[i][0].p}), last = dp[j];
        }
    }

    // 分组背包
    memset(dp, 0, sizeof(dp));
    for(int i=1;i<=m;i++) {
        for(int k=V;k>=0;k--) {
            for(int j=0;j<Main[i].size();j++) {
                int v = Main[i][j].v, p = Main[i][j].p;
                if(k>=v)
                    dp[k] = max(dp[k], dp[k-v]+p);
            }
        }
    }
    printf("%d\n", dp[V]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/izcat/p/11721073.html