题意:求得到p个点最少删除多少条边。p个点事连通的。
题解:dp【u】【j】 以u为根得到j个点需要的最大价值。
对于一个子节点 v 两种选择,要和不要。
不要的话,就删除u与v相连的边。 即dp【u】【j】+=1;
要的话,即进行背包 dp【u】【j】=min(dp【u】【j】,dp【u】【j-k】+ dp【v】【k】);
对于节点 u 我们没有考虑它的父节点,在最后求解时,dp【i】【p】加上1 (根节点除外),找个最小的即为结果。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int N=152;
const int inf=0x3f3f3f3f;
vector<int>V[N];
int dp[N][N],in[N];
int p;
void dfs(int u)
{
for(int i=0;i<=p;i++)
dp[u][i]=inf;
dp[u][1]=0;
for(int i=0;i<(int)V[u].size();i++)
{
int v=V[u][i];
dfs(v);
for(int j=p;j>=0;j--)
{
dp[u][j]+=1;//减去子树 v
for(int k=1;k<j;k++)
{
dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]);//不减去子树 v.
}
}
}
}
int main()
{
//freopen("Input.txt","r",stdin);
int n,i;
while(~scanf("%d%d",&n,&p))
{
for(i=0;i<=n;i++) V[i].clear();
memset(in,0,sizeof(in));
memset(dp,0,sizeof(dp));
for(i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
V[a].push_back(b);
in[b]=1;//入度标记
}
for(i=1;i<=n;i++)
if(in[i]==0)
break;//找根节点
dfs(i);
int ans=dp[i][p];//除根节点外的点要作为根节点要减去与父节点相连的边,所以加1,
for(int i=1;i<=n;i++)
if(ans>dp[i][p]+1)
ans=dp[i][p]+1;
printf("%d\n",ans);
}
return 0;
}