重建道路(洛谷-P1272)

题目描述

一场可怕的地震后,人们用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

思路:树形dp

使用 vector<int> g[N],用 g[i] 来保存所有结点,用 f[i][j] 来代表结点 i 保留 j 个结点最少需要删去的边的个数,则有状态转移:f[x][j]=min(f[x][j],f[x][j-k]+f[y][k]-1),其中 y 是 x 的子结点

源代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 1001
#define MOD 10007
#define E 1e-6
#define LL long long
using namespace std;
vector<int> g[N];
int son[N],sum[N];
int f[N][N];
void treeDP(int x){
    sum[x]=1;
    int num=g[x].size();//儿子的个数
    if(!num){
        f[x][1]=0;
        sum[x]=1;
        return;
    }
    for(int i=0;i<num;i++){//枚举每个儿子
        int y=g[x][i];
        treeDP(y);
        sum[x]+=sum[y];
        for(int j=sum[x];j>=0;j--)
            for(int k=1;k<j;k++)
                f[x][j]=min(f[x][j],f[x][j-k]+f[y][k]-1);
    }
}
int main()
{
    int n,p;
    cin>>n>>p;
    for(int i=1;i<n;i++){
        int x,y;
        cin>>x>>y;
        g[x].push_back(y);
        son[x]++;//记录儿子个数
    }
    memset(f,INF,sizeof(f));
    for(int i=1;i<n;i++)//只剩一个结点时,所有的儿子都减掉
        f[i][1]=son[i];

    treeDP(1);
    int res=f[1][p];
    for(int i=2;i<=n;i++)
        res=min(res,f[i][p]+1);
    cout<<res<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值