还是那句话。。。DP真恶心。。。
这道题大概就是添加子树时由于子树和根节点有边相连,于是每一种情况都要+1,然后再取min
DP方程真的很好写啊。。。就是边界和循环太恶心了
注意就是dp[x][0]是没有任何意义的,因为至少根节点会被选取,所以下限个数为1。然后就是需要注意若当前根节点不为整体棵树的root,则必须断掉来路,ans+1。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=155;
struct edge
{
int to,next;
}e[maxn<<1];
int n,m,cnt;
int size[maxn],in[maxn],head[maxn],son[maxn];
int dp[maxn][maxn];
void insert(int a,int b)
{
e[++cnt].to=b;e[cnt].next=head[a];head[a]=cnt;in[b]++;
}
void dfs(int x)
{
size[x]=1;
dp[x][1]=0;
for(int i=head[x];i;i=e[i].next)
{
dfs(e[i].to);
size[x]+=size[e[i].to];
for(int j=size[x];j>=1;j--)
{
dp[x][j]++;//所有加一,因为添加一个新子节点,若还要维护原值则一定要多删去这个与新子节点相连的边
for(int k=1;k<=min(j-1,size[e[i].to]);k++)
dp[x][j]=min(dp[x][j],dp[x][j-k]+dp[e[i].to][k]);
//k从1起,因为子树中至少要选一个点
//k<j,保证不越界,k!=j因为dp[x][0]是无意义的
//k<=size[e[i].to]因为在此子树中只能选择这么多点
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b);
}
memset(dp,63,sizeof dp);
int ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)
if(!in[i])dfs(i),ans=dp[i][m];
for(int i=1;i<=n;i++)if(in[i])
ans=min(ans,dp[i][m]+1);//非根节点的子节点若选择则还需要断掉和根节点相连的那一条边
printf("%d",ans);
return 0;
}