P1273 有线电视网(树上分组背包)

P1273 有线电视网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

书上分组背包一般形式:

vector<int> h[N]; // 存图
void dfs(int u)
{
    for(auto i : h[u]) // 枚举物品
    {
        dfs(i); // 往下接着搜
        for(int j = m; j ; --j) //枚举体积
        {
            for(int k = 1; k <= j; ++j) // 枚举每个子树体积
            //转移方程
        }
    }
}

本题[i][j]含义,表示i节点,选j个用户,能得到的钱的最大值,然后对每个节点做分组背包。
怎么转移
首先,背包的总容量相当于该点为根节点的子树中所有的用户数量(dp[i][j]的 j 不可能超过它连接的所有用户数)。然后,把该节点的每个儿子看成一组,每组中的元素为选一个,选两个...选n个用户。

转移方程 f[i][j]=max(f[i][j],f[i][j-k]+f[v][k]-这条边的花费) i,j不解释了,v表示枚举到这一组(即i的儿子),k表示枚举到这组中的元素:选k个用户
最后输出f[1][i]>=0的i的最大值,所以反向枚举。
本题中决策时不能从最大体积决策, 要从实际含义出发
具体看代码:

#include<bits/stdc++.h>
using namespace std;
const int N =3010;
int f[N][N]; //f[i][j] 表示已i为根节点选了j个用户的最大价值
vector<pair<int,int>> h[N];//存图
int n, m;
int w[N];
int dfs(int u) // dfs返回的时树中用户用户个数
{
    if(u > n - m)
    {
        f[u][1] = w[u];
        return 1;
    }
    int s = 0; // s为树总用户个数, 即背包体积
    for(auto [x, y] : h[u])
    {
        int v = dfs(x); // 子树体积
        s+=v;
        //分组背包
        for(int j = s; j ; --j)
        {
            for(int k = 1; k <= v && k <= j; ++k)
            {
                f[u][j] = max(f[u][j], f[x][k] + f[u][j - k] - y);
            }
        }
    }
    return s;
}
int main()
{
    memset(f, -0x3f, sizeof f);
    cin >>n >>m;
    for(int i = 1; i <= n; ++i) f[i][0] = 0;
    for(int i = 1; i <= n - m; ++i)
    {
        int k;
        cin >>k;
        while(k--)
        {
            int a, c;
            cin >>a >>c;
            h[i].push_back({a, c});
        }
    }
    for(int i = n - m + 1; i <= n; ++i) cin >>w[i];
    dfs(1);
    int ans = 0;
    for(int i = m; i >= 0; --i) // 反向输出
    {
        if(f[1][i] >= 0)
        {
            cout<<i<<endl;
            break;
        }
    }

}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值