POJ 1947 Rebuilding Roads(树形DP)

来源:互联网 发布:物联网与大数据的关系 编辑:程序博客网 时间:2024/06/06 04:59

Description
给出一棵有n个节点的树,问如何减去最少的边,得到一个含有p个节点的子树
Input
第一行两个整数n和p,之后n-1行每行两个整数a和b表示a和b间有一条边
Output
输出减去最少的边数
Sample Input
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
Sample Output
2
Solution
令dp[root][i]表示对root为根节点的树得到一棵有i个节点的子树需要减去的边数,那么对于节点i的一个儿子son有两种情况:
1.不去掉son子树
dp[root][i]=min(dp[root][j]+dp[son][i-j]) (0<=j<=i)
2.去掉son子树
dp[root][i]=dp[root][i]+1
综合一下就是dp[root][i]=min(dp[root][j]+dp[son][i-j],dp[root][i]+1) (0<=j<=i)
Code

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;#define maxn 151#define INF 0x3fint n,p,fa[maxn],son[maxn],bro[maxn],dp[maxn][maxn];void dfs(int root){    dp[root][1]=0;//初始化     int k=son[root];//枚举根节点的儿子们     while(k)     {        dfs(k);//深搜         for(int i=p;i>=1;i--)        {            int temp=dp[root][i]+1;            for(int j=1;j<i;j++)//转移方程                 temp=min(temp,dp[root][j]+dp[k][i-j]);            dp[root][i]=temp;        }        k=bro[k];//从k的兄弟节点继续转移     }}int main() {    while(~scanf("%d%d",&n,&p))    {        memset(son,0,sizeof(son));//初始化         memset(fa,0,sizeof(fa));        memset(dp,INF,sizeof(dp));        for(int i=1;i<n;i++)        {            int a,b;             scanf("%d%d",&a,&b);            fa[b]=a;//b的父亲是a             bro[b]=son[a];//b的兄弟节点是a的上一个儿子             son[a]=b;//a现在的儿子是b         }        int root;        for(int i=1;i<=n;i++)//找到根节点             if(!fa[i])            {                root=i;                break;            }        dfs(root);//从根节点开始转移         int ans=dp[root][p];        for(int i=1;i<=n;i++)            ans=min(ans,dp[i][p]+1);//任何一个不是根节点的节点欲成为根节点必须先切一条边         printf("%d\n",ans);    }    return 0;}
0 0
原创粉丝点击