暴力换根,对于每个根求出最长路径,取一个最小值。
每次求最长路径,还是上题的一个思想。
但是时间复杂度是O(n*n) , 过了7个数据。
/*
暴力换根,对于每个根求出最长路径,去一个最小值。
每次求最长路径,还是上题的一个思想。
但是时间复杂度是O(n*n) , 过了7个数据。
*/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010, M = N << 1, INF = 0x3f3f3f3f;
int n;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N];
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dfs(int u, int father)
{
d1[u] = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == father) continue;
int dist = dfs(j, u) + w[i];
if (dist >= d1[u]) d1[u] = dist;
}
return d1[u];
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d", &n);
for (int i = 1; i < n; i ++ )
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
int res = INF;
for (int i = 1; i <= n; i ++ ) res = min(res, dfs(i, -1));
printf("%d\n", res);
return 0;
}
这是一道很好的树形dp题,即用到了用子节点更新父节点信息的普遍情况,又用到了用父节点更新子节点的情况,所以掌握这一道题是很有必要的,废话不多说,下面开始讲讲我对y总的思路的理解
首先分析题目,其实是要我们把每一个点到其他点的最长距离求出来,再求一个其中最短的就可以了,我们来分析一下每一个点可以再树上怎么走,其实就是向上和向下走
我们用 d1[u],d2[u],up[u],p1[u],p2[u]分别存一下需要的信息,这些数据存的是:
d1[u]:存下u节点向下走的最长路径的长度
d2[u]:存下u节点向下走的第二长的路径的长度
p1[u]:存下u节点向下走的最长路径是从哪一个节点下去的
p2[u]:存下u节点向下走的第二长的路径是从哪一个节点走下去的
up[u]:存下u节点向上走的最长路径的长度
向下走是很容易的,dfs就可以了,那怎么向上走呢?其实向上走就是求一个点的父节点的不走该节点的最长路径,其实我们知道了每一个节点向下走的长度就可以知道向上的最长路径了,一个子节点 j 的向上最长路径就是 它的父节点 u 的最长向上路径和最长向下路径取最大值,如果向下最长路径经过了 j 就改为第二长的向下路径,对应代码:
if(p1[u]==j)up[j]=max(up[u],d2[u])+w[i];
else up[j]=max(up[u],d1[u])+w[i];
// /*
// 暴力换根,对于每个根求出最长路径,去一个最小值。
// 每次求最长路径,还是上题的一个思想。
// 但是时间复杂度是O(n*n) , 过了7个数据。
// */
// #include <iostream>
// #include <cstring>
// #include <algorithm>
// using namespace std;
// const int N = 10010, M = N << 1, INF = 0x3f3f3f3f;
// int n;
// int h[N], e[M], w[M], ne[M], idx;
// int d1[N], d2[N];
// void add(int a, int b, int c)
// {
// e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
// }
// int dfs(int u, int father)
// {
// d1[u] = 0;
// for (int i = h[u]; ~i; i = ne[i])
// {
// int j = e[i];
// if (j == father) continue;
// int dist = dfs(j, u) + w[i];
// if (dist >= d1[u]) d1[u] = dist;
// }
// return d1[u];
// }
// int main()
// {
// memset(h, -1, sizeof h);
// scanf("%d", &n);
// for (int i = 1; i < n; i ++ )
// {
// int a, b, c;
// scanf("%d%d%d", &a, &b, &c);
// add(a, b, c), add(b, a, c);
// }
// int res = INF;
// for (int i = 1; i <= n; i ++ ) res = min(res, dfs(i, -1));
// printf("%d\n", res);
// return 0;
// }
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010, INF = 1e9;
int d1[N], d2[N];
int up[N];
int p1[N], p2[N];
int h[N], ne[2*N], e[2*N], w[2*N], idx;
int n;
void add(int a, int b, int c)
{
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
// 返回u的最长向下路径
int dfs_down(int u, int father)
{
d1[u] = d2[u] = -INF;
for(int i=h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j == father) continue;
int dist = dfs_down(j, u) + w[i];
if(dist > d1[u])
{
d2[u] = d1[u], d1[u] = dist;
// 记录一下最长子树路径是经过那个子节点的
p2[u] = p1[u], p1[u] = j;
}
else if(dist > d2[u])
{
d2[u] = dist;
p2[u] = j;
}
}
//如果没有改变过该点的距离,
// 就证明这个点是叶节点
if(d1[u]==-INF)
d1[u]=d2[u]=0;
return d1[u];
}
// 子节点向上经过父节点的最长路径
void dfs_up(int u, int father)
{
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
if(j==father)continue;
//如果从父节点向下的最长路径经
// 过了要更新的子节点,
// 那么就用第二长的路径更新
// 否则,直接更新。
if(p1[u]==j)up[j]=max(up[u],d2[u])+w[i];
else up[j]=max(up[u],d1[u])+w[i];
// 向下处理子节点
dfs_up(j,u);
}
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
for(int i=0; i<n-1; i++)
{
int a, b, c;
cin >> a >> b >> c;
add(a, b, c), add(b, a, c);
}
dfs_down(1, -1);
dfs_up(1, -1);
int res = INF;
for(int i=1; i<=n; i++) res = min(res, max(d1[i], up[i]));
cout << res << endl;
return 0;
}