换根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 
#include  
#include 
#include  
#include  
#include  
#include 
#include 
#include  
#include  
#include 
#include 
#include  
#include  
#include 
using namespace std;
const int N = 1e6 + 7;  
​
vector 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 1e5 + 7;
vector 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;
}

你可能感兴趣的:(c++,图论)