题意:
实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.
Input
输入文件包含多组测试数据。对于每组测试数据,第一行一个整数N (N<=10000),接下来有N-1行,每一行两个数,对于第i行的两个数,它们表示与i号电脑连接的电脑编号以及它们之间网线的长度。网线的总长度不会超过10^9,每个数之间用一个空格隔开。
Output
对于每组测试数据输出N行,第i行表示i号电脑的答案 (1<=i<=N).
Sample Input
5
1 1
2 1
3 1
1 1
Sample Output
3
2
3
4
4
思路
这道题需要用到,树的最小直径知识点。不知道的可以先暂移百度
这道题还需要用到链式前向星的知识点。不知道的还可以先暂移百度
假设以上两个知识点都精通,那么我们开始:
首先回忆一下求树的直径的方法
• 假设树的最长路的两个叶子节点为v1, v2,从任意一点 u 出发走到的最 远的点一定是(v1,v2)中的一点,然后再从 v1 或者 v2出发走到的最 远点一定是 v2 或者 v1。由此经过两次遍历就能找到最长路径。 • 这道题不是找最长路径,是找某个节点 x所能到达的最长路径。首先这个 节点 x 的最长路径要么是到 v1 的路径,要么就是到 v2 的路径。 •
由于不知道往哪边走才是最长的路径,所以需要分别从 v1, v2 点再次遍历 共需要三次遍历。
在这我们看似是四次遍历:找v1
,找v2
,从v1
开始遍历,从v2
开始遍历。
但是我们可以在找v2
的时候,也就是从v1
遍历找最远点的时候,我们会遍历所有的点找到最远点,此时我们可以顺路将从v1
开始遍历到所有的点的长度记录下来。
代码:
首先遍历树我们知道有DFS和BFS两种方法,以下分别是DFS、BFS的代码。
//DFS
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
struct edge
{
int u, v, w, next;
}; //链式前向星 存储
int n, head[10002], tot = 1; //n个点 head链式前向星的头 边的索引
edge map[20002]; //存边
void ini(int& n)
{
tot = 1;
for(int i=0; i<=n; i++)
{
head[i]=-1;
}
}
void addEdge(int& u, int& v, int& w)
{ //链式前向星的存储
map[tot].u = u;
map[tot].v = v;
map[tot].w = w;
map[tot].next = head[u];
head[u] = tot;
tot++;
}
int a[10002];
int b[10002];
int v1 = 0,v2=0;
void dfs(int u)
{//第一遍dfs 从随便一个顶点 找到最远点
for(int i=head[u];i!=-1;i=map[i].next)
{
if(a[map[i].v]==-1)
{
a[map[i].v]=a[map[i].u]+map[i].w;
if(a[map[i].v]> a[v1])
{//寻找距离最大点
v1=map[i].v;
}
dfs(map[i].v); //dfs递归
}
}
}
void dfs2(int u)
{//第二遍dfs 从第一个最远点开始找另一个最远点 且顺路记录从第一最远点到个点的距离
for(int i=head[u];i!=-1;i=map[i].next)
{
if(a[map[i].v]==-1)
{
a[map[i].v]=a[map[i].u]+map[i].w;
if(a[map[i].v]> a[v2])
{
v2=map[i].v;
}
dfs2(map[i].v);
}
}
}
void DFS(int u)
{//第三次dfs 记录从第二个点到个点的距离
for(int i=head[u];i!=-1;i=map[i].next)
{
if(b[map[i].v]==-1)
{
b[map[i].v]=b[map[i].u]+map[i].w;
DFS(map[i].v);
}
}
}
int main()
{
int v, w;
while (scanf("%d", &n) != EOF)
{
ini(n);
for (int i = 2; i <= n; i++)
{
scanf("%d %d", &v, &w);
addEdge(v, i, w);
addEdge(i, v, w);
}
tot--;
for(int i=0;i<=n;i++) a[i]=-1; //初始化 数组 一边记忆化递归用
a[1]=0;
dfs(1); //因为第一台电脑总是有 所以就从1开始找第一个最远点
for(int i=0;i<=n;i++) a[i]=-1; //初始化数组 用来记录第一组数据
a[v1]=0;
dfs2(v1);//第二遍dfs 用来记录第一组距离
for(int i=0;i<=n;i++) b[i]=-1;
b[v2]=0;
DFS(v2); //第三遍dfs 用来记录第二组距离
for (int i = 1; i <= n; i++)
{
printf("%d\n", max(a[i], b[i])); //找距离最大者
}
}
return 0;
}
//BFS 与上面类似 改为的dfs改为bfs
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
struct edge
{
int u, v, w, next;
};
int n, head[10002], tot = 1;
edge map[20002];
void ini(int& n)
{
tot = 1;
for(int i=0; i<=n; i++)
{
head[i]=-1;
}
}
void addEdge(int& u, int& v, int& w)
{
map[tot].u = u;
map[tot].v = v;
map[tot].w = w;
map[tot].next = head[u];
head[u] = tot;
tot++;
}
int a[10002];
int b[10002];
int v1 = 0,v2=0;
void bfs(int u)
{
for(int i=0; i<=n; i++)
{
a[i]=-1;
} //初始化
queue<int> q;
q.push(u);
a[u] = 0;
while (!q.empty())
{
int now = q.front();
q.pop();
for (int i = head[now]; i != -1; i = map[i].next)
{
if (a[map[i].v] == -1)
{
q.push(map[i].v);
a[map[i].v] = a[map[i].u] + map[i].w;
if ( a[map[i].v] > a[v1] )
{//找第一个最大点
v1 = map[i].v;
}
}
}
}
}
void bfs2(int u)
{
for(int i=0; i<=n; i++)
{
a[i]=-1;
}
queue<int> q;
q.push(u);
a[u] = 0;
while (!q.empty())
{
int now = q.front();
q.pop();
for (int i = head[now]; i != -1; i = map[i].next)
{
if (a[map[i].v] == -1)
{
q.push(map[i].v);
a[map[i].v] = a[map[i].u] + map[i].w;
if (a[map[i].v] > a[v2])
{//第二个最大点
v2 = map[i].v;
}
}
}
}
}
void BFS(int u)
{
for(int i=0; i<=n; i++)
{
b[i]=-1;
}
queue<int> q;
q.push(u);
b[u] = 0;
while (!q.empty())
{
int now = q.front();
q.pop();
for (int i = head[now]; i != -1; i = map[i].next)
{
if (b[map[i].v] == -1)
{
q.push(map[i].v);
b[map[i].v] = b[map[i].u] + map[i].w; //记录第二组距离
}
}
}
}
int main()
{
int v, w;
while (scanf("%d", &n) != EOF)
{
ini(n);
for (int i = 2; i <= n; i++)
{
scanf("%d %d", &v, &w);
addEdge(v, i, w);
addEdge(i, v, w);
}
tot--;
bfs(1);
bfs2(v1);
BFS(v2);
for (int i = 1; i <= n; i++)
{
printf("%d\n", max(a[i], b[i]));
}
}
return 0;
}
以下是助教上课演示代码,用一个DFS操作实现三遍遍历。
用G[N]表示图,dis为每次遍历的距离,ans为最终答案。
s为距离最大点,刚开始为1,第一遍遍历为查找第一个最大距离点,第二次遍历从s也就是第一个最大距离点开始,查找第二个最大距离点,第三次遍历从第二个最大距离点开始遍历。
ans数组只有在距离最大值跟新时才更新。
总结:
刚开始觉得这道题是纯粹的树,边数等于节点数-1,所以开了和节点一样大小的数组存边,交了好几次都是Time Limit。修改了很多次都是这个结果,后来把它当成图来写,开了两倍数组存储边,就过了,然后回去将前面的代码的数组开大,发现都能AC;
哎,人生好难!!!为什么不报RE,还以为真的是算法有问。