题意:
给了一棵树,树上有n个结点,然后让求最少删去多少条边,可以使剩下的一棵子树中存在p个结点。
思路:
dp[x][ j ] 表示 把x结点当成根时,形成的恰好含有j个结点的子树,至少删除的边数。
此时考虑递推式;
对于x结点 的dp[x][j] 他的状态可以由子结点推出来,
对于孩子结点son来说;
1)当不需要son时 dp[x][j]= dp[x][j]+1; ///需要删去x与son的边
2)当需要son时 dp[x][ j ]= dp[ x ][ k ]+dp[ son ][ j-k ];
所以对两种情况取最优即可;
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <ctime>
#include <map>
#include <queue>
#define LL long long
#define INF 0x3f3f3f3f
#define bug puts("***************")
using namespace std;
const int N =200;
vector<int>vec[N];
bool vis[N];
int dp[N][N];
int father[N];
int n,m;
void DFS(int x){
dp[x][1]=0;
int len=vec[x].size();
for(int i=0;i<len;i++){
int son=vec[x][i];
DFS(son);
for(int j=m;j>=0;j--){ ///最多被用一次,所以倒着来,01背包,正着来就不对了。
int tmp=dp[x][j]+1;
for(int k=0;k<j;k++){
tmp=min(tmp,dp[x][k]+dp[son][j-k]);
}
dp[x][j]=tmp;
}
}
}
int main(){
memset(vis,0,sizeof(vis));
memset(dp,INF,sizeof(dp));
memset(father,0,sizeof(father));
int u,v;
scanf("%d%d",&n,&m);
for(int i=0;i<n-1;i++){
scanf("%d%d",&u,&v);
father[v]=1;
vec[u].push_back(v);
}
int root;
for(int i=1;i<=n;i++)
if(!father[i]){
root=i;
break;
}
DFS(root);
int ans=dp[root][m];
for(int i=1;i<=n;i++){ ///除了根结点,其他结点作为子树的根,需要+1 ,因为要与原来的树切断
ans=min(ans,dp[i][m]+1);
}
printf("%d\n",ans);
return 0;
}