【树形DP基础算法】重建道路

题目

https://www.luogu.org/problem/show?pid=1272

代码(想看的先跳)

分析

这道题毕竟是基础算法,很简单。设f[x][j]表示使得以x为根的j个节点的子树被剥离开来最少需要删除的边数。(我们知道DP有最重要的两个部分:临界值状态转移方程

临界值f[x][1]=du[x],其中du[x]表示所有与x相连的边数(包括连接父节点的那条),因为节点数为1就意味着只剩下节点x,所以连着的所有边都要切掉。

从题目中的样例来看,可以知道f[2][3]=2,切掉的是1->2和2->8;f[1][1]=4,删了下面的4条边。那么f[1][4]是多少呢?肯定和f[2][3]f[1][1]有关。通过观察、计算和瞎猜,我们可以知道f[1][4]=f[1][1]+f[2][3]-2=4

如图,要想把1的那1个节点和2的那3个节点连起来,有两刀是多余的。

所以设x的一个子节点为v,有状态转移方程f[x][j]=min(f[x][j],f[x][k]+f[v][j-k]-2)

(那可能有的小朋友们就要问了:万一f[x][k]的k个节点中有v呢?

不不不,那是不可能的。因为在枚举x的子节点的时候,f[x]已经通过前面的几个儿子得到了,所以在与v的第一次运算中不可能会有与f[v]重合的信息。)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,a,b,du[155],f[155][155],ans=0x3f3f3f3f;
vector<int>G[155];
void dfs(int x){
    f[x][1]=du[x];
    int u=G[x].size();
    for(int i=0;i<u;i++){
        int v=G[x][i];
        dfs(v);
        for(int j=m;j>1;j--)
            for(int k=1;k<j;k++){
                f[x][j]=min(f[x][j],f[x][k]+f[v][j-k]-2);
            }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++) 
            f[i][j]=200;
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        G[a].push_back(b),du[a]++,du[b]++;
    }
    dfs(1);
    for(int i=1;i<=n;i++)ans=min(ans,f[i][m]);
    printf("%d",ans);
    putchar('\n');
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值