1. 程式人生 > >[luogu]P1272 重建道路[樹形DP]

[luogu]P1272 重建道路[樹形DP]

一行 p12 有時 容易 時間 ont pri 農夫 san

[luogu]P1272

重建道路

——!x^n+y^n=z^n

題目描述

一場可怕的地震後,人們用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)的子樹將被分離出來


很容易想到樹形背包,用f[i][j]表示以i為根,刪得只剩下j個點的最少刪除。

f[i][j]=Min{f[v][k]+f[i][j-k]-2}(v為i的孩子)[(v,i)應該留下,可兩個決策都減了,要-2]。

初始化:f[i][1]=邊數,f[root][1]=邊數-1。

一開始理解錯題意,以為root一定要保留,結果就gg了,呵呵。

代碼:

 1 //2017.10.29
 2 //DP
 3 #include<iostream>
 4 #include<cstdio>
 5 #include<cstring>
 6 using namespace std;
 7 inline int read();
 8 int Min(int x,int y){return x<y?x:y;}
 9 namespace lys{
10     const int N = 150 + 7 ;
11     struct
edge{ 12 int to; 13 int next; 14 }e[N*3]; 15 int dp[N][N],pre[N],count[N],fa[N]; 16 int n,p,cnt,ans; 17 void add(int x,int y){e[++cnt].to=y;e[cnt].next=pre[x];pre[x]=cnt;} 18 void dfs(int node){ 19 int i,j,k,v; 20 dp[node][1]=count[node]+(fa[node]!=0); 21 for(i=pre[node];~i;i=e[i].next){ 22 v=e[i].to; 23 dfs(v); 24 for(j=p;j>=2;j--) 25 for(k=1;k<j;k++) 26 dp[node][j]=Min(dp[node][j],dp[v][k]+dp[node][j-k]-2); 27 } 28 ans=Min(dp[node][p],ans); 29 } 30 int main(){ 31 int i,u,v; 32 n=read(); p=read(); 33 memset(pre,-1,sizeof pre); 34 memset(dp,0x7,sizeof dp); 35 ans=dp[1][1]; 36 for(i=1;i<n;i++){ 37 u=read(); v=read(); 38 add(u,v); 39 count[u]++; 40 fa[v]=u; 41 } 42 for(i=1;i<=n;i++) if(!fa[i]){dfs(i);break;} 43 printf("%d\n",ans); 44 return 0; 45 } 46 } 47 int main(){ 48 lys::main(); 49 return 0; 50 } 51 inline int read(){ 52 int kk=0,ff=1; 53 char c=getchar(); 54 while(c<0||c>9){ 55 if(c==-) ff=-1; 56 c=getchar(); 57 } 58 while(c>=0&&c<=9) kk=kk*10+c-0,c=getchar(); 59 return kk*ff; 60 }

[luogu]P1272 重建道路[樹形DP]