Link:http://poj.org/problem?id=1947
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
/*
Anniversary party POJ - 2342
題意:有n個點組成一棵樹,問至少要刪除多少條邊才能獲得一棵有p個結點的子樹?
題解:dp[i][j] 代表以i爲根節點,有j個節點的最小減邊數
dp[i][1] = 與子節點的邊數+1(與父節點邊);
for(int k = 1; k < j; k++) 枚舉子節點貢獻的點個數
dp[i][j] = min(dp[i][j],dp[child[i]][k]+dp[i][j-k]-2) 加上這條於子節點相連的邊
(要減2,因爲算dp[child][j]這條邊算了一次,dp[root][1]這條邊也算了一次)
最後答案dp[root][j]有點不同,他與父節點相連的一條邊減去的,實際上不需要減(因爲他沒父節點)
*/
const int N = 155;
const int INF = 0x3f3f3f3f;
vector<int> tree[N];
int n,p;
bool isroot[N];
int dp[N][N];
void dfs(int root)
{
int len = tree[root].size();
dp[root][1] = len+1;
for(int i = 2; i <= p; i++)
dp[root][i] = INF;
for(int i = 0; i < len; i++)
{
int u = tree[root][i];
dfs(u);
for(int j = p; j > 1; j--) //01揹包的思路
{
for(int k = 1; k < j; k++)
dp[root][j] = min(dp[root][j],dp[u][k]+dp[root][j-k]-2);
}
}
}
int main(){
scanf("%d%d",&n,&p);
int u,v;
for(int i = 1; i < n; i++)
{
scanf("%d%d",&u,&v);
tree[u].push_back(v);
isroot[v] = 1;
}
int root = 1;
while(isroot[root])
root++;
dfs(root);
dp[root][p]--;
int mi = INF;
for(int i = 1; i <= n; i++)\
mi = min(mi,dp[i][p]);
printf("%d\n",mi);
return 0;
}