HDU 2196 Computer(求树上每个点的最长距离)
题意:
这题想了挺久的, 参考了kuangbin大神的代码:https://www.cnblogs.com/kuangbin/archive/2012/08/28/2659915.html
给出树上边的长度, 求出每一个点的最长距离(就是求这个点到某一个叶子结点的距离, 这个距离最长)。
分析:
结点u的最长路径, 其实就是max( u到子树叶子的最长路径, u到父亲的距离 + 父亲子树的最长路径).
注意, 因为父亲子树的最长路径可能会经过u, 这样这个状态就不能用 u到父亲的距离 + 父亲子树的最长路径表示。
所以记录每个节点的最长路和次长路。
用两次dfs求解。
第一次dfs求出每个点只看子树的最长距离和次长距离。(只关注结点本身)
第二次dfs求出每个孩子结点的最长距离(答案)。(关注结点的孩子)
松弛条件可以看代码。
#include<stdio.h> #include<vector> #include<algorithm> #include<string.h> #include<iostream> using namespace std; const int maxn = 12000 + 7; const int inf = 1e9 + 7; int Max[maxn];// 最大距离 int sMax[maxn];// 次大距离 int Id[maxn];// 最大距离对应序号 int sId[maxn];// 次大距离对应序号 struct Edge { int v, d; }; vector<Edge> G[maxn]; int N; void init() { for(int i = 0; i < maxn; i ++) { G[i].clear(); } } void dfs1(int u, int pre) {//更新u本身 Max[u] = sMax[u] = 0; for(int i = 0; i < G[u].size(); i++) { int v = G[u][i].v, d = G[u][i].d; if(v == pre) continue; //不经过父亲, 只搜索子树 dfs1(v, u); //一直搜到叶子再回溯, 因为是根据孩子的Max更新自己的Max if(sMax[u] < Max[v] + d) { //如果u的次长距离 < 到v的距离 + v的最大距离 //更新距离 sMax[u] = Max[v] + d; sId[u] = v; if(sMax[u] > Max[u]) { //如果次长距离大于最长距离, 交换二者位置 swap(sMax[u], Max[u]); swap(sId[u], Id[u]); } } } } int dfs2(int u, int pre) {//更新u的孩子 for(int i = 0; i < G[u].size(); i++) { int v = G[u][i].v, d = G[u][i].d; if(v == pre) continue; //同样不经过父亲 if(v == Id[u]) { //如果v在u的最长路径上 if(sMax[u] + d > sMax[v]) { //看看u的次长路 + d 是否> v的次长路 sMax[v] = sMax[u] + d; sId[v] = u; if(sMax[v] > Max[v]) { //如果次长距离大于最长距离, 交换二者位置 swap(sMax[v], Max[v]); swap(sId[v], Id[v]); } } } else { // v不在u的最长路径上 if(d + Max[u] > sMax[v]) { //试着更新v sMax[v] = d + Max[u]; sId[v] = u; if(sMax[v] > Max[v]) { //如果次长距离大于最长距离, 交换二者位置 swap(sMax[v], Max[v]); swap(sId[v], Id[v]); } } } dfs2(v, u); } } int main() { while(cin >> N) { init(); for(int i = 2 ; i <= N; i++) { int u, v, d; cin >> v >> d; G[i].push_back((Edge){v,d}); G[v].push_back((Edge){i,d}); } dfs1(1, -1); dfs2(1, -1); for(int i = 1; i <= N; i++) { cout << Max[i] << "\n"; } } return 0; }