andre_joy

导航

poj 1947

地址:http://poj.org/problem?id=1947

题意:给你一棵树,求最少剪掉几条边使能够得到一个p个节点的树。

mark:因为节点个数不多,我们可以把节点个数当成一个状态,那么dp[i][j]代表以i为根的子树要变成j个节点需要剪掉的边数。

    这里我们考虑状态转移的时候要注意,,我们考虑子节点的时候不用考虑它与父节点的那条边,就当不存在,那么最后求的的最小边数加1就ok了~

    考虑一个节点时,有两种选择,要么剪掉跟子节点相连的边,则dp[i][j] = dp[i][j]+1;

                  要么不剪掉,则d[i][j] = max(dp[i][j], dp[i][k]+dp[son][j-k]);

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

const int INF = 10000000;
const int N = 160;
int n,p,root;
int head[N],son[N],dp[N][N];
bool f[N];

int min(int a, int b) {return  a < b ? a : b;}

void dfs(int v)
{
    int i,j,k;
    for(i = 0; i <= p; i++) dp[v][i] = INF;
    dp[v][1] = 0;
    int s = son[v];
    while(s)
    {
        dfs(s);
        for(i = p; i >= 0; i--)
        {
            int tmp = dp[v][i]+1;
            for(j = 1; j < i; j++)
                tmp = min(tmp, dp[v][j]+dp[s][i-j]);
            dp[v][i] = tmp;
        }
        s = head[s];
    }
}

int main()
{
    int i,j,k;
    while(~scanf("%d%d", &n, &p))
    {
        memset(son, 0, sizeof(son));
        memset(f, 1, sizeof(f));
        for(i = 1; i < n; i++)
        {
            scanf("%d%d", &j, &k);
            head[k] = son[j];
            son[j] = k;
            f[k] = 0;
        }
        for(i = 1; i <= n; i++)
            if(f[i]) {root = i;break;}
        dfs(root);
        int ans = dp[root][p];
        for(i = 1; i <= n; i++)
            ans = min(ans, dp[i][p]+1);
        printf("%d\n", ans);
    }
    return 0;
}

posted on 2012-10-09 22:41  andre_joy  阅读(613)  评论(2编辑  收藏  举报