POJ 4045 - Power Station(树形DP)

题目:

http://poj.org/problem?id=4045

题意:

n个节点,n-1条边。求出以哪个点作为基地时,其他点到这个点的总权值最小。输出最小的总权值以及基地。

思路:

树形DP

num[u]: u点的所有子树节点个数。dp[u][0]: u点的所有子树节点到u的总距离。

dp[u][1]: 除u点的所有子树节点以外的节点到u点的总距离。

f[v][1] = (f[u][0]-f[v][0]-num[v]) + f[u][1] + n - num[v] (画个图就知道了)

AC.

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn = 50005;
vector<int> g[maxn];
int n;
int num[maxn], vis[maxn];
long long dp[maxn][2];

void init()
{
    for(int i = 0; i <= n; ++i) {
        g[i].clear();
    }
    memset(num, 0, sizeof(num));
    memset(vis, 0, sizeof(vis));
    memset(dp, 0, sizeof(dp));
}

void dfs(int u)
{
    vis[u] = 1;
    num[u] = 1;
    for(int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if(vis[v]) continue;
        dfs(v);
        num[u] += num[v];
        dp[u][0] += dp[v][0];
    }
    dp[u][0] += num[u]-1;
    return;
}
long long ans;
void ddfs(int u)
{
    vis[u] = 1;
    for(int i = 0; i < g[u].size(); ++i) {
        int v = g[u][i];
        if(vis[v]) continue;
        dp[v][1] = (dp[u][0] - dp[v][0] - num[v]) + dp[u][1] + n-num[v];
        //printf("(%d, %d) %d %d %d %d\n", u, v, dp[u][0], dp[v][0], num[v], dp[u][1]);
        ddfs(v);
    }
    ans = min(ans, dp[u][0] + dp[u][1]);
}
int main()
{
    //freopen("in", "r", stdin);
    int T;
    scanf("%d", &T);
    while(T--) {
        int I, R;
        scanf("%d %d %d", &n, &I, &R);

        init();
        for(int i = 0; i < n-1; ++i) {
            int u, v;
            scanf("%d %d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }

        dfs(1);

        memset(vis, 0, sizeof(vis));
        ans = 1e18;
        //printf("%I64d\n", ans);
        ddfs(1);

        bool fir = 0;
        printf("%I64d\n", ans*I*I*R);
        for(int i = 1; i <= n; ++i) {
            if(dp[i][0]+dp[i][1] == ans) {
                if(!fir) {
                    fir = 1;
                    printf("%d", i);
                }
                else {
                    printf(" %d", i);
                }
            }
        }
        printf("\n\n");
    }
    return 0;
}

另一种思路:

树的重心的定义:子树节点<= n/2, 而每一棵树的重心不会超过2个。

所以求出树的重心之后以重心为根遍历整棵树求出距离。

http://acm.hust.edu.cn/vjudge/contest/viewSource.action?id=4156687

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值