树形DP的第一题吧,前几道都是根据树形结构求解。
给出N个节点,N-1条边,要求获得一棵p个节点的树最少需要断掉多少边。
对于每一个节点的子树有舍弃这个子树,获得一部分两种操作。
dp[s][i] = min (min(dp[s][j]+dp[k][i-j]) , dp[s][i]+1 )
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
#include <iomanip>
#define inf (1<<29)
using namespace std;
int n,p,root;
int son[2000];
int father[200];
int brother[200];
int dp[200][200];//以第i个节点为根时有j个节点需要断掉的最小的边
void dfs(int root)//搜索以某个点为根时的结果
{
int k,temp;
for(int i=0;i<=p;i++)
{
dp[root][i]=inf;
}
dp[root][1]=0;//到叶子节点时刚好为1.
k=son[root];//对子树进行选取
while(k)
{
dfs(k);
for(int i=p;i>=1;i--)
{
temp=dp[root][i]+1;//舍弃这棵子树
for(int j=1;j<i;j++)
{
temp=min(temp,dp[k][i-j]+dp[root][j]);//不舍弃这棵子树
}
dp[root][i]=temp;//以root为根的i个节点的树最小需要断掉的根
}
k=brother[k];//下一棵子树
}
}
int solve()
{
int ans;
dfs(root);
ans=dp[root][p];//以根节点为根
for(int i=1;i<=n;i++)
{
ans=min(ans,dp[i][p]+1);//以其余节点为根要+1
}
return ans;
}
int main()
{
int u,v;
while(scanf("%d%d",&n,&p)!=EOF)
{
memset(father,0,sizeof(father));
memset(son,0,sizeof(son));
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
father[v]=1;
brother[v]=son[u];
son[u]=v;
}
for(int i=1;i<=n;i++)
{
if(!father[i])
{
root=i;
break;
}
}
printf("%d\n",solve());
}
return 0;
}