标签:
经典树形dp:问在一棵树上最少删除多少条边可以分离出一个节点数为p的子树。
定义状态:
dp[i][j]表示从i为根的子树上分离出一个节点数为j的子树的代价(最少需要删除的边数)。
考虑i节点的每个儿子ii,ii可以选或者不选(切断),然后就转化成了背包问题。
dp[u][j] = min( dp[u][j], dp[u][j - k] + dp[v][k] );
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int INF = 9999999; 7 const int N = 151; 8 int dp[N][N]; 9 int head[N]; 10 int f[N]; 11 int n, p, e; 12 13 void init() 14 { 15 e = 0; 16 memset( f, -1, sizeof(f) ); 17 memset( head, -1, sizeof(head) ); 18 } 19 20 struct Edge 21 { 22 int v, next; 23 } edge[N]; 24 25 void addEdge( int u, int v ) 26 { 27 edge[e].v = v; 28 edge[e].next = head[u]; 29 head[u] = e++; 30 } 31 32 void dfs( int u ) 33 { 34 dp[u][1] = 0; 35 for ( int i = 2; i <= p; i++ ) 36 { 37 dp[u][i] = INF; 38 } 39 for ( int i = head[u]; i != -1; i = edge[i].next ) 40 { 41 int v = edge[i].v; 42 dfs(v); 43 for ( int j = p; j >= 1; j-- ) 44 { 45 dp[u][j]++; 46 for ( int k = 1; k <= j - 1; k++ ) 47 { 48 dp[u][j] = min( dp[u][j], dp[u][j - k] + dp[v][k] ); 49 } 50 } 51 } 52 } 53 54 int main () 55 { 56 while ( scanf("%d%d", &n, &p) != EOF ) 57 { 58 init(); 59 for ( int i = 1; i < n; i++ ) 60 { 61 int u, v; 62 scanf("%d%d", &u, &v); 63 addEdge( u, v ); 64 f[v] = u; 65 } 66 int root; 67 for ( root = 1; f[root] != -1; root = f[root] ) ; 68 dfs(root); 69 int ans = dp[root][p]; 70 for ( int i = 1; i <= n; i++ ) 71 { 72 if ( i == root ) continue; 73 ans = min( ans, dp[i][p] + 1 ); 74 } 75 printf("%d\n", ans); 76 } 77 return 0; 78 }
标签:
原文地址:http://www.cnblogs.com/huoxiayu/p/4657468.html