题目描述
一场可怕的地震后,人们用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;
}