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

2024-01-13 02:28

本文主要是介绍换根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、子节点2cin >> val >> begin >> end;  // 输入节点权值、子节点1、子节点2Val[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] == 0if (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;
}

这篇关于换根DP求树的重心/求最小距离和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/599948

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]