UVALive 4614 Moving to Nuremberg

本文介绍了一种使用两次深度优先搜索(DFS)解决特定图论问题的方法。首次DFS用于计算子树中节点到根节点的成本,第二次DFS则计算整个树中各节点到根节点的成本总和。通过这种方式可以有效地找到最优路径及其成本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://livearchive.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2615


DFS twice

the first DFS

the dp[u] as the time that edge[father][u] had been used

sum[u] as summary cost  given by  the cost of all nodes in subtree u to u

so

for each son of u

dp[u] <- dp[v] + dp[u]

sum[u] <- sum[v] + dp[v] * w[u][v]


the second time DFS

sum[u] as summary cost given by  the cost of all nodes in whole tree to u

so

sum[u]  <- sum[u] + sum[fa] - sum[u] + (sdp - dp[u] - dp[u]) * w[fa][u]          sdp as the total times to visit all node


#include <iostream>

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <map>

using namespace std;
#define MAXN 55000
#define Rep(c, a, b) for (int c = a; c < b; c++)
#define INFI 1e16
typedef long long LL;
typedef pair<LL, LL> PA;

struct  Edge
{
    int v, w, next;
};
vector<Edge> E;
int L[MAXN];
void G_ini()
{
    E.clear();
    memset(L, -1, sizeof L);
}
void AE(int u, int v, int w)
{
    Edge te = {v, w, L[u]};
    L[u] = E.size();
    E.push_back(te);
}

int n, m;
LL mn, sdp, dp[MAXN], sum[MAXN];

bool vis[MAXN];
PA DFS_U(int u)
{
    LL tp = 0, ts = 0;
    vis[u] = 1;
    for (int i = L[u]; i != -1; i = E[i].next)
    {
        int v = E[i].v, w = E[i].w;
        if (!vis[v])
        {
            PA td = DFS_U(v);
            tp += td.first;
            ts += td.first * w + td.second;
        }
    }
    dp[u] += tp;
    sum[u] = ts;
    return make_pair(dp[u], sum[u]);
}

void DFS_D(int u, int e)
{
    vis[u] = 1;
    if (e != -1)
    {
        int fa = E[e ^ 1].v, w = E[e].w;
        sum[u] += sum[fa] - sum[u] - dp[u] * w;
        sum[u] += E[e ^ 1].w * (sdp - dp[u]);
    }
    mn = min(mn, sum[u]);
    for (int i = L[u]; i != -1; i = E[i].next)
    {
        int v = E[i].v;
        if (!vis[v]) DFS_D(v, i);
    }
}


int main()

{
    int Cas;

    scanf("%d", &Cas);
    while (Cas--)
    {
        G_ini();
        scanf("%d", &n);
        Rep(i, 0, n - 1)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            AE(u, v, w);
            AE(v, u, w);
        }
        scanf("%d", &m);
        memset(sum, 0, sizeof sum);
        memset(dp, 0, sizeof dp);
        sdp = 0;
        while (m--)
        {
            int u, w;
            scanf("%d%d", &u, &w);
            sdp += w;
            dp[u] = w;
        }
        memset(vis, 0, sizeof vis);
        DFS_U(1);
        memset(vis, 0, sizeof vis);
        mn = INFI;
        DFS_D(1, -1);
        printf("%lld\n", mn * 2);
        int i;
        for (i = 1; i <= n; i++) if (sum[i] == mn)
        {
            printf("%d", i);
            break;
        }
        for (i++; i <= n; i++) if (sum[i] == mn)
            printf(" %d", i);
        puts("");
    }


    return 0;

}


beside DFS once is enough :)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值