洛谷P1272重建道路 题解

题目链接

分析:

明显的树形背包
f u , i f_{u,i} fu,i表示节点 u u u的子树保留 i i i个节点所需要删去的最小边数(计算 u u u与父节点的连边)。转移时对每一个点跑一边背包即可。我们依次枚举 u u u的所有儿子,令当前枚举到的儿子为 v v v,有方程(注意不取 v v v时为原来的 f u , i f_{u,i} fu,i,需单独拿出来):
f u , i = m i n ( f u , i , min ⁡ j = 1 i − 1 { f u , i − j + f v , j − 2 } )    ( i = 1 , 2 , ⋯   , p ) f_{u,i} = min(f_{u,i},\min_{j = 1}^{i - 1}\{f_{u,i - j} + f_{v,j} - 2\})\;(i = 1,2,\cdots,p) fu,i=min(fu,i,j=1mini1{fu,ij+fv,j2})(i=1,2,,p)

初始化显然: f u , 1 = d u    ( u = 1 , 2 , ⋯   , n ) f_{u,1} = d_u\;(u = 1,2,\cdots,n) fu,1=du(u=1,2,,n),其中 d u d_u du表示点 u u u的度数。



为什么要减二?

因为 f v , j f_{v,j} fv,j中计算了点 v v v与父亲(即点 u u u)的连边, f u , i f_{u,i} fu,i中也计算了点 u u u与点 v v v的连边(原本不包含点 v v v),而若取了点 v v v u u u v v v的连边是不用被删去的,所以这条边被多计算了两边(这也是为什么不取 v v v时要单独拿出来)。




要注意倒序枚举 i i i,最终答案为 max ⁡ u = 1 n { f u , p } \max\limits_{u = 1}^n\{f_{u,p}\} u=1maxn{fu,p}

Code:

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 200,inf = 1e9;
int n,p,x,y,cnt,ans = inf,d[maxn],last[maxn],f[maxn][maxn];
struct edge{
    int v,nxt;
}e[2 * maxn];
int read(){
    int x = 0;
    char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + (c ^ 48),c = getchar();
    return x;
}
inline void insert(int x,int y){
    cnt ++,e[cnt].v = y,e[cnt].nxt = last[x],last[x] = cnt;
}
void dfs(int u,int fa){
    f[u][1] = d[u];
    for(int i = last[u]; i; i = e[i].nxt){
        int v = e[i].v;
        if(v == fa) return;
        dfs(v,u);
        for(int j = p; j >= 1; j --)//倒序枚举
            for(int k = 1; k < j; k ++)
                f[u][j] = min(f[u][j],f[v][k] + f[u][j - k] - 2);
    }
}
int main(){
    n = read(),p = read();
    for(int i = 1; i <= n; i ++)
        for(int j = 0; j <= p; j ++)
            f[i][j] = inf;
    for(int i = 1; i < n; i ++){
        x = read(),y = read(),d[x] ++,d[y] ++;
        insert(x,y),insert(y,x);
    }
    dfs(1,0);//任选一点为根即可
    for(int i = 1; i <= n; i ++) ans = min(ans,f[i][p]);
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值