大概的题意是给出n,p,一共有n个节点,问你要剩下p个节点,最少减去最少的边是多少。
dp[root][i]:记录root结点,要得到一棵i个节点的子树去掉的最少边数
详细解释看代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 #define N 160 6 #define INF 0x3ffffff 7 8 int dp[N][N],son[N],bro[N],fa[N]; 9 int root,n,p; 10 11 void dfs(int root) 12 { 13 int tem; 14 for(int i=0; i<=p; i++) 15 dp[root][i] = INF; 16 17 dp[root][1] = 0;//在root处想得到[i]个节点的子树所要去掉的最少边数 18 int k = son[root];//考虑其儿子k 19 /* 20 切去k子树:tem = dp[root][i] + 1 21 不切掉k子树:tem = min(dp[k][i-j]+dp[root][j],tem) 22 */ 23 while(k) 24 { 25 dfs(k); 26 for(int i=p; i>=1; i--)//类似背包的处理 27 { 28 tem = dp[root][i] + 1;//在root处想得到i个节点子树时,进行剪掉子树 29 30 for(int j=1; j<i; j++) 31 tem = min(dp[k][i-j]+dp[root][j],tem);//不剪去第k个子树,然后的到所需节点数的边数dp[k][i-j]+dp[root][j] 32 33 dp[root][i] = tem; 34 } 35 k = bro[k]; 36 } 37 } 38 39 int solve() 40 { 41 dfs(root); 42 int ans = dp[root][p]; 43 for(int i=1; i<=n; i++) 44 ans = min(dp[i][p]+1,ans); 45 printf("%d\n",ans); 46 return ans; 47 } 48 int main() 49 { 50 //freopen("in.txt","r",stdin); 51 int a,b; 52 while(~scanf("%d%d",&n,&p) ) 53 { 54 memset(fa,0,sizeof(fa)); 55 memset(son,0,sizeof(son)); 56 //memset(brother,0,sizeof(brother)); 57 58 for(int i=1; i<n; i++) 59 { 60 scanf("%d%d",&a,&b);//brother[i]记录i节点的儿子数,brother[i]记录i节点的兄弟数 61 fa[b] = 1;//标记有父亲不是根 62 bro[b] = son[a];//记录同一个父亲的上一个兄弟 63 son[a] = b;//该父亲现在的儿子 64 } 65 /* 66 for(int i=1; i<12; i++) 67 { 68 printf("~~fa[%d]=%d ",i,fa[i]); 69 printf("~~bro[%d]=%d ",i,bro[i]); 70 printf("~~son[%d]=%d\n",i,son[i]); 71 } 72 */ 73 for(int i=1; i<=n; i++)//找到根节点 74 { 75 if(fa[i] == 0) 76 { 77 root = i; 78 break; 79 } 80 } 81 82 solve(); 83 } 84 return 0; 85 }