洛谷P1273有线电视网(树上背包)

传送门

一、题解

  1. 这是一道树上有依赖背包,我们将它转化为分组背包进行求解。
  2. 我们设 d p [ i ] [ u ] [ j ] dp[i][u][j] dp[i][u][j]表示以 u u u为祖先的子树上,前i个儿子满足j个用户的最大收益。
  3. 我们就可以写出转移方程, d p [ i ] [ u ] [ j ] = m a x ( d p [ i ] [ u ] [ v ] , d p [ i − 1 ] [ v ] [ j − k ] + d p [ u 当 前 儿 子 的 v 的 最 大 个 数 ] [ v ] [ k ] − w ) dp[i][u][j] = max(dp[i][u][v],dp[i-1][v][j - k] + dp[u当前儿子的v的最大个数][v][k] - w) dp[i][u][j]=max(dp[i][u][v],dp[i1][v][jk]+dp[uv][v][k]w)
  4. 我们自底向上进行dfs,然后进行dp,这样我们可以将其转化为0/1背包,并且可以压缩一个维度,即 d p [ u ] [ j ] = m a x ( d p [ u ] [ v ] , d p [ v ] [ j − k ] + d p [ v ] [ k ] − w ) dp[u][j] = max(dp[u][v],dp[v][j - k] + dp[v][k] - w) dp[u][j]=max(dp[u][v],dp[v][jk]+dp[v][k]w),注意 j j j要逆向循环,这样才能使得物品只选择一次。

二、ACcode

/*
 * @Author: NEFU_马家沟老三
 * @LastEditTime: 2020-09-24 20:05:15
 * @CSDN blog: https://blog.csdn.net/acm_durante
 * @E-mail: 1055323152@qq.com
 * @ProbTitle: 
 */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define lowbit(x) ((x) & -(x))
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define mem(a, b) memset(a, b, sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const double PI = acos(-1.0);
int n,m;//n结点总数,m用户终端的数量
int head[3005],cnt;
struct node
{
    int v,w,next;
}e[6007];
int val[3005];
void add(int u,int v,int w){
    e[++cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt;  
}
int dp[3005][3005];//以u为父亲节点,满足前i个用户的最大收益

int dfs(int u,int fa){
    if(u > n - m){//用户节点
        dp[u][1] = val[u];
        return 1;
    }
    int leaves = 0;//以u为父亲节点的树上,用户的数量
    for(int i = head[u]; ~i ; i = e[i].next){
        int v = e[i].v, w = e[i].w;
        if(fa == v) continue;
        int tmp = dfs(v,u);//以v节点为根的子树上用户的数量
        leaves += tmp;
        for(int j = leaves ; j > 0 ; j--){//状态-->背包数量->以父亲为子树下的用户数量
            for(int k = 0;k <= tmp; k++){//决策-->选择k个用户
                if(j - k >= 0)
                    dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k] - w);
            }
        }
    }
    return leaves;
}
int main()
{
    mem(head,-1);
    mem(dp,-127);//初始化    
    scanf("%d%d",&n,&m);
    rep(i,1,n - m){
        int k,v,w;
        scanf("%d",&k);
        rep(j,1,k){
            scanf("%d%d",&v,&w);
            add(i,v,w);
        }
    }
    rep(i,n - m + 1,n)
        scanf("%d",&val[i]);
    rep(i,1,n) dp[i][0] = 0;//不选择用户的话就是0
    dfs(1,0);
    per(i,0,m){
        if(dp[1][i] >= 0){
            printf("%d\n",i);
            break;
        }
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值