[LUOGU1272] 重建道路

题目描述

一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。John想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目。

输入输出格式

输入格式:

第1行:2个整数,N和P

第2..N行:每行2个整数I和J,表示节点I是节点J的父节点。

输出格式:

单独一行,包含一旦被破坏将分离出恰含P个节点的子树的道路的最小数目。

输入输出样例

输入样例#1: 
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
输出样例#1: 
2

说明

【样例解释】

如果道路1-4和1-5被破坏,含有节点(1,2,3,6,7,8)的子树将被分离出来


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int n, m;
struct edge
{
    int nxt, to;
}ed[310];
int head[155], cnt;
inline void add(int x, int y) {ed[++cnt]=(edge){head[x], y};head[x]=cnt;}
int vis[155];int root;
int f[155][155];
int siz[155];
int son[155];
inline int dfs(int x)
{
    siz[x] = 1;
    for (register int i = head[x]; i; i = ed[i].nxt)
    {
        int to = ed[i].to;
        siz[x] += dfs(to);
    }
    return siz[x];
}

inline void dp(int x)
{
    for (register int i = head[x]; i; i = ed[i].nxt)
    {
        int to = ed[i].to;
        dp(to);
        for (register int t = siz[x] ; t >= 0 ; t --)
        {
            for (register int j = 1 ; j < t ; j ++)
            {
                f[x][t] = min(f[x][t], f[x][t-j] + f[to][j] -1);
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (register int i = 1; i < n; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y);
        vis[y]++;
        son[x]++;
    }
    for (register int i = 1; i <= n; i++) if (!vis[i]) {root=i;break;}
    dfs(root);
    memset(f, 0x3f, sizeof f);
    for (register int i = 1; i <= n; i++) f[i][siz[i]] = 0, f[i][1] = son[i];
    dp(root);
    int ans = f[root][m];
    for (register int i = 2; i <= n; i++) ans = min(ans, f[i][m]+1);
    cout<<ans;
    return 0;
}
原文地址:https://www.cnblogs.com/BriMon/p/9261929.html