树形DP pku1947

其实不管什么DP,本质思想都是不变的,围绕着最优子结构展开思路,最终记录下最优的结果,所以写好状态转移,处理好边界问题,还是关键手段。

状态:dp[i][j]表示以i为根的子树孤立出 j 个点要去掉的最少的边,

在树上的动态规划一般都是父结点和子节点的关系,所以在思考状态转移时时应该尽量把父结点的子节点考虑进去

分别以不同的点为根进行深搜

先要想到我们最后要得到的结果应该是dp[i][p]中的最小值

而dp[i][j]是从dp[u][k]和dp[i][j-k]推过来的,u为i的子节点

还有就是以i为根和以i的子节点为u根时,dp[u][k]+dp[i][j-k]多加了两次他们之间的边,所以要减去2

即dp[root][p]=min(dp[root][p],  dp[u][k]+dp[root][p-k]-2);(其中u为root的一个孩子)

View Code
#include<stdio.h>
#include
<string.h>
#define N 160
int min(int a,int b)
{
return a<b?a:b;
}
struct node{
int to,next;
}edge[
2*N];
int tot=0,n,p;
int head[N],ans[N],dp[N][N];
void add(int a,int b)
{
edge[tot].to
=b;
edge[tot].next
=head[a];
head[a]
=tot++;
}
void DP(int root,int fa)
{
int i,j,k,u;
for(i=head[root];i!=-1;i=edge[i].next)
{
u
=edge[i].to;
if(fa!=u) DP(u,root);
}
for(i=head[root];i!=-1;i=edge[i].next)
{
u
=edge[i].to;
if(u==fa) continue;
for(j=p;j>=1;j--)
{
for(k=1;k<j;k++)
dp[root][j]
=min(dp[root][j],dp[u][k]+dp[root][j-k]-2);
}
}
}
int main()
{
int x,y,i,j;
while(scanf("%d%d",&n,&p)!=EOF)
{
memset(head,
-1,sizeof(head));
memset(ans,
0,sizeof(ans));
for(i=0;i<n-1;i++)
{
scanf(
"%d%d",&x,&y);
add(x,y);
add(y,x);
ans[x]
++;
ans[y]
++;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
dp[i][j]
=N;
}
}
for(i=1;i<=n;i++)
dp[i][
1]=ans[i];
DP(
1,0);
int mi=N;
for(i=1;i<=n;i++)
mi
=min(mi,dp[i][p]);
printf(
"%d\n",mi);
}
return 0;
}

你可能感兴趣的:(pku)