poj1947
题目大意:给n(n<=150)个点的一棵树,求删掉最少边数k使得最后该树只剩下p(1<=p<=n)个节点。(求最小的k)
解题思想:每次删掉一条边,也就是每次删掉一个子树。那么最后剩的那个有p个节点的子树一定可以看成是最初的那棵树的连通部分。但是由于我们不知道如何选取合适的根保证当前这个点一定不是最优解中要删掉的。考虑到点数为150,这样就很明显了,就是枚举根找最优解!
难点:假设dp[u][c]表示u为根的子树中删得最后只剩c个节点最小删边数。这里每次要删掉一棵子树,如假设u有孩子节点v1,v2,v3……。要删掉子树v1,结果值dp[u][x]需要加1。或者反着想,dp[u][1]=count(Vi),删掉u的所有直接孩子。然后试着把v1接回来,每接上一个Vi,dp[u][x]就减1,不就是很明显的0,1背包了吗?
没想到竟然一A了。。。
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 #define N 151 6 struct edge{ 7 int u,v,next; 8 edge(int e1=0,int e2=0,int e3=0){ u=e1,v=e2,next=e3; } 9 }e[N*2]; 10 int ec,head[N]; 11 void add_edge(int u,int v){ 12 e[ec]=edge(u,v,head[u]); head[u]=ec++; 13 e[ec]=edge(v,u,head[v]); head[v]=ec++; 14 } 15 int dp[N][N]; 16 int ncnt[N], scnt[N]; //当前子树上有多少节点,当前节点有多少孩子 17 int dfs_defineTheMap(int u,int p=0){ 18 ncnt[u]=1; 19 scnt[u]=0; 20 for(int i=head[u];i>-1;i=e[i].next) 21 if(e[i].v != p) 22 ncnt[u] += dfs_defineTheMap(e[i].v,u), scnt[u]++; 23 24 for(int i=ncnt[u];i>0;i--) dp[u][i]=N; 25 //dp[u][0]=1; 26 dp[u][1]=scnt[u]; //把孩子节点都删掉 27 int maxc=1; 28 for(int i=head[u];i>-1;i=e[i].next) 29 if(e[i].v != p){ 30 int v=e[i].v; 31 for(int j=maxc;j>0;j--) 32 for(int s=ncnt[v];s>0;s--)//背包 33 dp[u][j+s]=min(dp[u][j+s],dp[u][j]+dp[v][s]-1); 34 maxc += ncnt[v]; 35 } 36 return ncnt[u]; 37 } 38 int main() 39 { 40 int n,np,root; 41 while(scanf("%d%d",&n,&np) != EOF){ 42 ec=0; 43 memset(head,-1,sizeof(head)); 44 int u,v; 45 for(int i=1;i<n;i++) 46 scanf("%d%d",&u,&v), add_edge(u,v); 47 //if(np >= n) { printf("0\n"); continue; } 48 int ans=n; 49 for(root=1;root<=n;root++){ 50 dfs_defineTheMap(root); 51 for(int u=1;u<=n;u++) 52 if(ncnt[u] >= np) 53 ans = min(ans,1+dp[u][np]); 54 ans = min(ans,dp[root][np]); 55 } 56 printf("%d\n",ans); 57 } 58 return 0; 59 }