http://poj.org/problem?id=1947
Description
The cows have reconstructed Farmer John's farm, with its N barns (1 <= N <= 150, number 1..N) after the terrible earthquake last May. The cows didn't have time to rebuild any extra roads, so now there is exactly one way to get from any given barn to any other barn. Thus, the farm transportation system can be represented as a tree.
Farmer John wants to know how much damage another earthquake could do. He wants to know the minimum number of roads whose destruction would isolate a subtree of exactly P (1 <= P <= N) barns from the rest of the barns.
Farmer John wants to know how much damage another earthquake could do. He wants to know the minimum number of roads whose destruction would isolate a subtree of exactly P (1 <= P <= N) barns from the rest of the barns.
Input
* Line 1: Two integers, N and P
* Lines 2..N: N-1 lines, each with two integers I and J. Node I is node J's parent in the tree of roads.
* Lines 2..N: N-1 lines, each with two integers I and J. Node I is node J's parent in the tree of roads.
Output
A single line containing the integer that is the minimum number of roads that need to be destroyed for a subtree of P nodes to be isolated.
Sample Input
11 6 1 2 1 3 1 4 1 5 2 6 2 7 2 8 4 9 4 10 4 11
Sample Output
2
Hint
[A subtree with nodes (1, 2, 3, 6, 7, 8) will become isolated if roads 1-4 and 1-5 are destroyed.]
/**
树形DP + 背包(题解说明为转载)
本题要求求最少删除几条边使得子树节点个数为p,我们只要算出每个以节点i为根的树中节点个数为p的最少删除边数,求个最小值就好。
每棵以i为根的树有sum种物品(sum为他以及与他的子孙节点的个数),必须要删除k条边才能使得这棵子树有j个节点(1<=j<=sum),
那么每个物品j的费用是k,价值是j,这样问题就转换为在树上的分组背包,总共有n组物品,每次都从以i为根的物品组中选择一个物品进行转移,
每组选择一个物品。由于根节点固定了是1,我把树看成有向的树,也就是每次求解都不管父节点,很多人的解题报告里都有管父亲节点,但我觉得那样不好理解。
现在设dp[i][j]表示以i为根的子树中节点个数为j的最少删除边数(从分组背包角度理解就是到转移到第i组价值为j的最少费用)
状态转移方程: dp[i][1] = tot(tot为他的子节点个数)
dp[i][j] = min(dp[i][j],dp[i][k]-1+dp[s][j-k]) (1<=i<=n,2<=j<=sum(节点总和),1<=k<j,s为i子节点)
(i中已有k个节点并从s中选择j-k个,算最少删除边数,s选上所以i->s的边不需删除,所以-1)
*/
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
using namespace std;
const int N=151;
struct node
{
int v,next;
} edge[N];
int ip,n,m,a,b,ans;
int dp[N][N],num[N],head[N],sum[N];
void init()
{
memset(num,0,sizeof(num));
memset(head,-1,sizeof(head));
memset(dp,0x3f3f3f3f,sizeof(dp));
memset(sum,0,sizeof(sum));
ip=0;
}
void addedge(int i,int j)
{
edge[ip].v=j;
edge[ip].next=head[i];
head[i]=ip++;
num[i]++;
}
void dfs(int k)
{
sum[k]=1;
if(head[k]==-1)
{
dp[k][1]=0;
return;
}
for(int i=head[k]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
dfs(v);
sum[k]+=sum[v];
for(int j=sum[k]; j>0; j--)
for(int p=1; p<j; p++)
dp[k][j]=min(dp[k][j],dp[k][j-p]+dp[v][p]-1);
}
}
int main()
{
init();
while(~scanf("%d%d",&n,&m))
{
for(int i=1; i<n; i++)
{
scanf("%d%d",&a,&b);
addedge(a,b);
}
for(int i=1; i<=n; i++)
dp[i][1]=num[i];
dfs(1);
ans=dp[1][m];
for(int i=2; i<=n; i++)
ans=min(ans,dp[i][m]+1);
printf("%d\n",ans);
}
return 0;
}