换根dp 树形dp

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
typedef long long LL;
LL ans[N];//记一下每个点作为根的答案
LL siz[N];//存每个节点的子节点包括它自己点的数量(包括它自己的子节点个数)
LL sum;
vector<int> v[N];//建立树
int n;

void dfs(int u,int dep,int fa)
{
    sum = sum + dep;
    for(int i = 0 ; i < v[u].size(); i++)
    {
        int to = v[u][i];
        if(to==fa) continue;
        dfs(to,dep+1,u);
        siz[u] = siz[u] + siz[to];
    }
}

void dfs1(int u,int fa)
{
    for(int i = 0 ; i <v[u].size(); i++)
    {
        int to = v[u][i];
        if(to == fa) continue;
        //以这个节点为根的总深度
        /*
         * 具体怎么算出来的现在来分析一下:
         *情况1:该节点包括它自己的所有子节点减一,因为它是从父节点移过来的
         * 它和它的子节点到它的距离都减小了
         * 情况2:当从父节点移动到它这里时除了它和它的子节点以外的节点都远离它了一个距离
         * 因为原本统计的是它的父亲节点,现在到它这里了,其他节点到这个节点要先到父节点再到它这里
         * 所以公式就是:父节点的总深度减去它和它的所有子节点的数量(因为每个都减1)
         * 再加上所有节点减去它和它的子节点的数量剩下的加上(因为其他的点到它这距离都加了1
         */
        ans[to] = ans[u]-siz[to] +(n-siz[to]);
        dfs1(to,u);
    }
    return ;
}

signed main()
{
    cin>>n;//n个点

    for(int i = 1 ; i <= n ; i ++ )
    {
        siz[i] = 1;
    }
    //存树
    for(int i = 1 ; i <= n ; i ++)
    {
        int a,b;
        cin>>a>>b;
        v[a].push_back(b);
        v[b].push_back(a);
    }

    sum = 0;
    dfs(1,0,0);//先处理以1作为根的答案,再转移到其他点
    ans[1] = sum;
    dfs1(1,0);
    LL ans1 = 0;
    for(int i = 1 ; i <= n ; i++)
    {
        ans1 = max(ans1,ans[i]);
    }
    cout<<ans1<<endl;
    return 0;
}

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

#define maxn 110000
#define ll long long

int n, c[maxn], dist[maxn]; // 定义全局变量
struct Edge {
    int to, dis; // 定义边的结构体,包含目标节点和距离
};

vector<Edge> edge[maxn]; // 定义邻接表,存储每个节点的边信息
int siz[maxn], head[maxn], cnt, tot; // 定义全局变量
void add(int from, int to, int dis) {
    edge[from].push_back({to, dis}); // 添加一条从from到to的边,距离为dis
    edge[to].push_back({from, dis}); // 添加一条从to到from的边,距离为dis
}

int sum[maxn];
ll f[maxn]; // 定义全局变量

void dfs(int x, int fa) {
    siz[x] = 1; // 初始化当前节点的大小为1
    sum[x] = c[x]; // 初始化当前节点的权值为c[x]
    for (const auto& e : edge[x]) { // 遍历当前节点的所有边
        int v = e.to; // 获取目标节点
        if (v == fa) continue; // 如果目标节点是父节点,则跳过
        dist[v] = dist[x] + e.dis; // 更新目标节点的距离
        dfs(v, x); // 递归处理目标节点
        siz[x] += siz[v]; // 更新当前节点的大小
        sum[x] += sum[v]; // 更新当前节点的权值
    }
}

void dfs1(int x, int fa) {
    for (const auto& e : edge[x]) { // 遍历当前节点的所有边
        int v = e.to; // 获取目标节点
        if (v == fa) continue; // 如果目标节点是父节点,则跳过
        f[v] = f[x] - sum[v] * e.dis + (tot - sum[v]) * e.dis; // 更新目标节点的值
    }
}

int main() {
    cin >> n; // 输入节点个数
    for (int i = 1; i <= n; i++) {
        cin >> c[i]; // 输入每个节点的权值
        tot += c[i]; // 计算总权值
    }
    for (int i = 1; i < n; i++) {
        int a, b, c;
        cin >> a >> b >> c; // 输入边的起始节点、结束节点和距离
        add(a, b, c); // 添加边
    }
    dfs(1, 0); // 从根节点开始进行深度优先搜索
    for (int i = 1; i <= n; i++) {
        f[1] += dist[i] * c[i]; // 更新根节点的值
    }
    dfs1(1, 0); // 再次进行深度优先搜索,更新其他节点的值
    ll ans = 101010101000; // 初始化答案为一个较大的数
    for (int i = 1; i <= n; i++) {
        ans = min(ans, f[i]); // 找到最小的节点值作为答案
    }
    cout << ans << endl; // 输出答案
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值