重建道路

题目描述
一场可怕的地震后,人们用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个节点的子树的道路的最小数目。
输入输出样例
输入样例#111 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
输出样例#12
说明
【样例解释】
如果道路1-4和1-5被破坏,含有节点(123678)的子树将被分离出来
题面

正解:树形DP

首先定义状态很重要,我就在一开始被卡死了.....

随便以一个点为根

f[i][j]表示以i为根,保留j个节点要砍掉的最少边数

初始化:首先极大值

f[i][0]=0,f[i][1]=与i相连的边数

设边  u - > v

f[u][j]=min(f[u][j],f[u][k]+f[v][j-k]-2)

为什么要减2?

答:我们通过v更新u,就要保留u->v 此条边,但是

想想我们的初始化,我们在f[u][k]中砍掉了这条边一次

在f[v][j-k]中砍掉了这条边一次,所以要加回来

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define DB double
 4 using namespace std;
 5 const int N=200;
 6 int n,in[N],h[N],tot;
 7 struct node{
 8     int u,v,ne;
 9 }e[N*N];
10 int f[N][N],p,ans;
11 void add(int u,int v)
12 {
13     in[v]++;in[u]++;
14     tot++;e[tot]=(node){u,v,h[u]};h[u]=tot;
15 }
16 void dfs(int u)
17 {
18     f[u][1]=min(f[u][1],in[u]);
19     for(int i=h[u];i;i=e[i].ne)
20     {
21         int rr=e[i].v;
22         dfs(rr);
23         for(int j=p;j>=1;--j)
24          for(int k=1;k<j;++k)
25           f[u][j]=min(f[u][j],f[rr][j-k]+f[u][k]-2);
26     }
27 }
28 int main()
29 {
30     scanf("%d%d",&n,&p);
31     for(int i=1,x,y;i<=n-1;++i)
32     {
33         scanf("%d%d",&x,&y);
34         add(x,y);
35     }
36     memset(f,127/3,sizeof(f));
37     for(int i=1;i<=n;++i) f[i][0]=0;
38     dfs(1);ans=N;
39     for(int i=1;i<=n;++i) ans=min(ans,f[i][p]);
40     printf("%d",ans);
41     return 0;
42 }
View Code
posted @ 2018-04-18 15:27  月亮茶  阅读(692)  评论(0编辑  收藏  举报