HDOJ 4003 Find Metal Mineral(树形DP)

题意:

一棵有权树,从根结点中放入 K 个机器人,求用这 K 个机器人遍历所有的结点最少的权值和。

思路:

1. dp[u][i] 表示给以 u 为根节点的子树放 i 个机器人,遍历其子树所需要的最小权值。

2. 关键在于 dp[u][0] 的理解,表示:最后停留在以 u 为根节点的子树下 0 个机器人,并且遍历了 u 子树的最小权值和。

3. 下面的步骤就变成和分组背包类似的情况了,根节点 u 给孩子 v 放多少个机器人。

4. dp[u][i] = min(dp[u][i], dp[u][j] + dp[v][i-j] + (i-j) * c); 可以理解成给 v 放了 i-j 个机器人,给 v 的其他兄弟放了 j 个,u 总共有 i 个

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 10010;
const int MAXD = 11;

struct edge {
    int v, c;
    edge* next;
} *V[MAXN], ES[MAXN * 2];

int EC, N, S, K, dp[MAXN][MAXD];
bool vis[MAXN];

void addedge(int u, int v, int c)
{
    ES[++EC].next = V[u];
    V[u] = ES + EC; 
    V[u]->v = v, V[u]->c = c;
}

void initdata()
{
    EC = 0;
    memset(V, 0, sizeof(V));
    memset(vis, false, sizeof(vis));

    for (int i = 0; i < N - 1; ++i)
    {
        int x, y, w;
        scanf("%d %d %d", &x, &y, &w);
        addedge(x, y, w);
        addedge(y, x, w);
    }
}

void treedp(int u)
{
    vis[u] = true;
    memset(dp[u], 0, sizeof(dp[0]));

    for (edge* e = V[u]; e; e = e->next)
    {
        int v = e->v, c = e->c;

        if (vis[v])
            continue ;

        treedp(v);

        for (int i = K; i >= 0; --i)
        {
            dp[u][i] += dp[v][0] + 2 * c;
            for (int j = 0; j <= i; ++j)
                dp[u][i] = min(dp[u][i], dp[u][j] + dp[v][i-j] + (i-j) * c);
        }
    }
}

int main()
{
    while (scanf("%d %d %d", &N, &S, &K) != EOF)
    {
        initdata();
        treedp(S);
        printf("%d\n", dp[S][K]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kedebug/p/2934041.html