POJ 1947 Rebuilding Roads(树形DP)

题意:

给定 n 个点的树,最少删多少边,会有棵 p 个节点的树。

思路:

1. dp[u][p] 表示以 u 为根节点保留 p 个节点最少删除的边数。初始化的地方比较巧妙:dp[u][1] = 0,其他赋值 INFS

2. 如果保留 u 的孩子节点 v,则有 dp[u][p] = min(dp[u][p - k] + dp[v][k]),如果不保留则有 dp[u][p] = dp[u][p] + 1

3. 此题的时间复杂度为 O(p* n) ,不能采取有交集的泛化物品的并的 O(p * n) 的解法,因为对于子树,相对的背包容量并不固定。

 

#include <iostream>

#include <algorithm>

using namespace std;



const int MAXN = 160;

const int INFS = 0x3fffffff;

int dp[MAXN][MAXN], U[MAXN], V[MAXN];

bool vis[MAXN];



void treedp(int u, int vol, int n)

{

    for (int v = 0; v <= vol; ++v)

        dp[u][v] = INFS;

    dp[u][1] = 0;



    for (int i = 1; i < n; ++i)

    {

        if (u != U[i])

            continue ;



        treedp(V[i], vol, n);

        for (int v = vol; v >= 0; --v)

        {

            int ans = INFS;

            if (dp[u][v] != INFS)

                ans = dp[u][v] + 1;



            for (int p = 0; p <= v; ++p)

                if (dp[u][p] != INFS && dp[V[i]][v - p] != INFS)

                    ans = min(ans, dp[u][p] + dp[V[i]][v - p]);



            dp[u][v] = ans;

        }

    }

}



int main()

{

    int n, p;

    while (scanf("%d %d", &n, &p) != EOF)

    {

        memset(vis, false, sizeof(vis));

        for (int i = 1; i < n; ++i)

        {

            scanf("%d %d", &U[i], &V[i]);

            vis[V[i]] = true;

        }



        int rt;

        for (int i = 1; i <= n; ++i)

            if (!vis[i])

                rt = i;



        treedp(rt, p, n);



        int ans = dp[rt][p];

        for (int i = 1; i <= n; ++i)

            if (dp[i][p] < ans)

                ans = dp[i][p] + 1;



        printf("%d\n", ans);

    }

    return 0;

}

你可能感兴趣的:(Build)