[刷题笔记] Luogu P1064 [NOIP2006 提高组] 金明的预算方案

Problem

Analysis

我们发现如果忽略主从关系,那这道题就是一个裸的 01 背包问题。

主从关系处理也非常简单,借鉴 P2014 选课 的经验,转换成树上背包问题。

同理,本题是一个森林,若将 0 号节点参与建树的话就可以把森林转换成树,处理方便。

具体地,设 \(f_{i,j}\) 表示以 \(i\) 为父节点,剩余价钱(这里看作重量)为 \(j\) 时的最大价值。显然我们需要预处理出价值 = 重要度 \(\times\) 价钱。

树上背包具体实现的时候需要先处理子节点。

Code

/*

有依赖关系的dp统一用树上背包处理。
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 40000;
int n,m;
int f[200][N];
int w[N],v[N];
vector <int> Edge[N];
void dp(int noww,int t,int fa) //树上背包类似于记搜
{
    if(t <= 0) return; //这个参数是必须的!和选课不同
    for(int i=0;i<Edge[noww].size();i++)
    {
        int vv = Edge[noww][i]; 
        if(vv == fa) continue; //不能回去
        for(int j=n-w[vv];j>=0;j--) f[vv][j] = f[noww][j] + v[vv]; //只有先取now才能取用vv,先后顺序需要注意。题目保证一个儿子只对应一个父亲。这是初始确定的。非常重要。另外以儿子为根节点,儿子一定取,后面所能使用的重量上限是 n-w[儿子]。
        dp(vv,t-w[vv],noww); // 先搜儿子,树上dp板子
        for(int j = n;j >= w[vv];j--) // 01背包倒着搜
        {
            f[noww][j] = max(f[noww][j],f[vv][j-w[vv]]); //我们已经预处理完了,直接取即可 
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++) 
    {
        int v1,p,q;
        cin>>v1>>p>>q;
        Edge[q].push_back(i); //双向建边
        Edge[i].push_back(q);
        v[i] = v1*p; //预处理每件物品的价值
        w[i] = v1;
    }
    m++; 
    dp(0,n,0);  //虚拟节点参与,变森林为树qwq(和选课类似)
    int maxn = 0;
    for(int i=0;i<=n;i++)
    {
        maxn = max(maxn,f[0][i]); // 我们把森林变成树,回到 0 号虚拟节点。我们只是不知道重量,枚举即可。
    }
    cout<<maxn<<endl;
    return 0;
}
posted @   SXqwq  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示