换根DP求树的重心/求最小距离和

DP过程

const int N = 1e6+7;
int Size[N] // Size[i]表示以i为根的子树的结点数
int dp[N] //dp[i]表示树中所有点到结点i的距离和
    
dp[son]=dp[pos]+(Size[1]-Size[son])-Size[son];
//状态转移方程

预处理

  • 预处理出所有Size dfs

  • 预处理出dp[1] dfs

洛谷P1364 医院设置

[P1364 医院设置 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)](https://www.luogu.com.cn/problem/P1364)

分析

如果你非要用换根dp做的话。

那么这道题与模板的变化点就在于我们并不是要求树中点到某点的距离和最小

而是要求所有居民走的距离最短

但换汤不换药,我们只要将每个Size[i] 初始化为i的点权就可以了

理由是:

当我们换根 i->j 时, j 的子树内所有的居民少走1个单位距离, j的子树外的所有居民多走1个单位距离。仍然与我们的换根dp实质相同

代码

cpp
#define _CRT_SECURE_NO_WARNINGS  
#include<iostream> 
#include<cstdio>  
#include<cmath> 
#include<string>  
#include<cstring>  
#include<algorithm>  
#include<vector> 
#include<cctype> 
#include<map>  
#include<set>  
#include<queue> 
#include<numeric> 
#include<iomanip>  
#include<stack>  
#include<list> 
using namespace std;
const int N = 1e6 + 7;  
​
vector<int> v[N]; 
int Size[N]; 
int Val[N];  
long long dp[N];  
​
​
void get_size(int pos, int depth, int fa) {
    Size[pos] = Val[pos]; // 节点pos的初始子树大小为节点权值
    
    for (int i = 0; i < v[pos].size(); i++) { // 遍历节点pos的所有子节点
        int son = v[pos][i];  // 获取子节点
        if (son != fa) {  // 如果子节点不是父节点,则递归计算子节点的子树大小
            get_size(son, depth + 1, pos);
            Size[pos] += Size[son];  // 累加子节点的子树大小到父节点
        }
    }
    dp[1] += depth * Val[pos];  // 计算动态规划值,累加节点深度乘以权值
}
​
long long ans = 2147483647;  // 初始化最小值为int型最大值
​
// 动态规划
void DP(int pos, int fa) {
    for (int i = 0; i < v[pos].size(); i++) {  // 遍历节点pos的所有子节点
        int son = v[pos][i];  // 获取子节点
        if (son != fa) {  // 如果子节点不是父节点,则进行动态规划
            dp[son] = dp[pos] + Size[1] - 2 * Size[son];  // 更新子节点的动态规划值
            DP(son, pos);  // 递归调用动态规划
        }
    }
    ans = min(ans, dp[pos]);  // 更新最小值
}
​
int main() {
    int n;  // 节点数量
    cin >> n;  // 输入节点数量
    ans *= ans;  // 将最小值平方,初始化为一个足够大的数
​
    for (int i = 1; i <= n; i++) {  // 遍历每个节点
        int val, begin, end;  // 节点权值、子节点1、子节点2
        cin >> val >> begin >> end;  // 输入节点权值、子节点1、子节点2
        Val[i] = val;  // 存储节点权值
​
        if (begin) {  // 如果子节点1存在
            v[i].push_back(begin);  // 将子节点1添加到节点i的邻接表中
            v[begin].push_back(i);  // 将节点i添加到子节点1的邻接表中
        }
​
        if (end) {  // 如果子节点2存在
            v[i].push_back(end);  // 将子节点2添加到节点i的邻接表中
            v[end].push_back(i);  // 将节点i添加到子节点2的邻接表中
        }
    }
​
    get_size(1, 0, -1);  // 计算每个节点的子树大小
    DP(1, 1);  // 进行动态规划
    cout << ans;  // 输出最小值
}

洛谷P1395会议

比上一题还简单,没意思的模板题。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cctype>
#include<map>
#include<set>
#include<queue>
#include<numeric>
#include<iomanip>
#include<stack>
#include<list>
using namespace std;
const int N = 1e5 + 7;
vector<int> v[N];
​
int Size[N];  int d[N];
int use[N];
int n;
​
void get_size(int now,int fa) {
​
    Size[now] = 1;
​
    for (int i = 0; i < v[now].size(); i++) {
        if (v[now][i] != fa) {
            get_size(v[now][i], now);
            Size[now] += Size[v[now][i]];
        }
    }
    
}
​
// d[now] = d[fa]+ Size[1]-2*Size[now] ;
​
int get_d1(int now,int fa,int depth) {
    int ans = 0;
​
    for (int i = 0; i < v[now].size(); i++) {
        if (v[now][i] != fa) {
            
            ans+=get_d1(v[now][i], now,depth+1)+depth;
            
        }
    }
    return ans;
}
​
void get_alld(int now, int fa) {
    use[now] = 1;
    
​
    for (int i = 0; i < v[now].size(); i++) {
        int son = v[now][i];
        //and use[son] == 0
        if (son != fa and use[son] == 0) {
            d[son] = d[now] + Size[1] - 2 * Size[son];
            get_alld(son, fa);
        }
    }
}
// 6+4-6=4
int main() {
    
    cin >> n;
    if (n == 0) {
        cout << 0<<' '<<0;
        return 0;
    }
​
    for (int i = 1; i <= n - 1; i++) {
​
        int a, b;
        cin >> a >> b;
​
        v[a].push_back(b);
        v[b].push_back(a);
​
    }
​
    get_size(1, 0);
​
    d[1]=get_d1(1, 0,1);
    
​
    get_alld(1, 0);
​
    int minn = 0x7fffffff;
    int pos = 0;
​
    for (int i = 1; i <= n; i++) {
        if (d[i] < minn) {
            pos = i;
            minn = d[i];
        }
    }
    cout << pos << ' ' << minn;
}
  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

louisdlee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值