c++树的最大直径——氪金带东

题意:

实验室里原先有一台电脑(编号为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,还以为真的是算法有问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
贪心算法是一种问题求解方法,它在每一步总是做出当前情况下的最优选择,以期望获得最优解。而"最大整数"同样可以使用贪心算法来求解。 对于"最大整数"的问题,我们可以考虑如下的贪心策略:从高位开始,尽可能选择较大的数字。具体步骤如下: 1. 对于给定的整数,我们首先将其转化为一个数组,其中每个元素表示整数的一个位数。 2. 从最高位(最左侧)开始,遍历数组。 3. 对于当前位上的数字,从9开始递减,找到第一个小于等于当前数字的最大数字。 4. 如果找到了符合条件的最大数字,将其放在当前位。否则,不做任何操作。 5. 继续向下遍历,重复步骤3-4。 6. 最终,得到的数组即为满足条件的最大整数。 以一个具体的例子说明上述算法:假设给定的整数为5372。 1. 将整数转化为数组[5, 3, 7, 2]。 2. 从最高位开始遍历。 3. 对于第一位5,从9开始递减,找到第一个小于等于5的数字,为7。 4. 将7放在第一位,得到[7, 3, 7, 2]。 5. 对于第二位3,从9开始递减,找到第一个小于等于3的数字,为3(与当前数字相等)。 6. 不做任何操作,得到[7, 3, 7, 2]。 7. 对于第三位7,从9开始递减,找到第一个小于等于7的数字,为7。 8. 将7放在第三位,得到[7, 3, 7, 2]。 9. 对于第四位2,从9开始递减,找到第一个小于等于2的数字,为2。 10. 将2放在第四位,得到[7, 3, 7, 2]。 11. 遍历结束,最终得到的数组为[7, 3, 7, 2],转化为整数为7372。 通过上述贪心算法,我们得到了满足条件的最大整数7372。证明了贪心算法在"最大整数"问题中的有效性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值