1207. 大臣的旅费 Java题解 (树的直径,dfs,bfs)【第四届蓝桥杯省赛C++A组,JAVA A组】

35 篇文章 2 订阅
26 篇文章 1 订阅

输入样例:

5 
1  2  2 
1  3  1 
2  4  5 
2  5  4 

输出样例:

135

解题思路:

因为“从首都到达每个城市的距离都是唯一的”,所以由城市构成的图事实上是无环图,而无环图就是一棵树。

花费的路费为 10s + s(s +1) / 2 ,当距离s为有意义的正数时,路费是随着距离s单调递增的,所以求路费的最大值,就是求在两城市间距离的最大值。

在给定一棵树后,求树中长度最长的路径就是求树的直径。求树的直径分两步:①任取一点x,求其余点距离x最远的点y,此时的y就是树直径的一个端点。②求其余点距离y最远的点z,yz之间的距离即为树的直径。(可以通过抽象作图的方法证明)

因为图中点较稀疏,所以用邻接表建图。

找两城市的距离既可以用深搜,也可以用广搜,深搜时,因为图为双向图,为了避免结点被重复计算,即可以每次记录父结点,也可以用去重数组判断,每次将父结点的距离加上自身的距离;广搜时,将每个城市入队,用去重数组避免重复计算。

最坏情况下,当城市连成一条链时,距离为10^8,路费会爆int,需要用long存储。

Java代码:(dfs)

import java.util.*;

public class Main {
	static ArrayList<Node> []city;  //  邻接表
	static int []dis;  //  每个城市可以到达的最远距离
	
	static class Node{
		int x;  //  城市编号
		int w;  //  距离
		
		public Node(int x, int w) {
			this.x = x;
			this.w = w;
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		
		city = new ArrayList[n + 1];  //  用全局变量
		dis = new int[n + 1];
		for(int i = 1; i <= n; i++)
			city[i] = new ArrayList<Node>();
			
		for(int i = 0; i < n - 1; i++) {
			int x = scan.nextInt(), y = scan.nextInt(), w = scan.nextInt();
			city[x].add(new Node(y, w));  //  双向图
			city[y].add(new Node(x, w));
		}
		scan.close();
		
		dfs(1, -1, 0);  //  找树的直径的一个端点
		
		int index = 1;
		for(int i = 1; i <= n; i++)
			if(dis[index] < dis[i])
				index = i;
				
		Arrays.fill(dis, 0);
		dfs(index, -1, 0);  //  找最大距离
		
		int s = 1;
		for(int i = 1; i <= n; i++)
			if(dis[i] > s) s = dis[i];
		
		System.out.println(10 * s + (1l + s) * s / 2);
	}
	
	public static void dfs(int u, int pre, int d) {  //  需要记录父结点
		for(Node edge: city[u]) {
			if(edge.x != pre) {  
				dis[edge.x] = d + edge.w;
				dfs(edge.x, u, dis[edge.x]);
			}
		}
	}
}

Java代码:(bfs)

import java.util.*;

public class Main {
	static int n;
	static ArrayList<Node> []city;  //  数组中的每个元素都表示一条单链表
	static int []dis;
	
	static class Node{
		int x, w;
		public Node(int x, int w) {
			this.x = x;
			this.w = w;
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		n = scan.nextInt();
		city = new ArrayList [n + 1];
		dis = new int[n + 1];
		
		for(int i = 1; i <= n; i++)
			city[i] = new ArrayList<>();
		
		for(int i = 1; i < n; i++) {
			int x = scan.nextInt(), y = scan.nextInt(), w = scan.nextInt();
			city[x].add(new Node(y, w));
			city[y].add(new Node(x, w));
		}
		scan.close();
		
		bfs(1);
		int index = 1;
		for(int i = 1; i <= n; i++)  //  找直径的一个端点
			if(dis[index] < dis[i]) index = i;
		
		Arrays.fill(dis, 0);
		bfs(index);
		int s = dis[1];
		for(int i = 1; i <= n; i++)
			if(s < dis[i]) s = dis[i];
		
		System.out.println(10 * s + s * (s + 1l) / 2);
	}
	
	public static void bfs(int u) {
		boolean []vis = new boolean[n + 1];
		Queue<Integer> qu = new LinkedList<>();  //  队列中存放城市的编号
		qu.add(u);
		vis[u] = true;
		while(!qu.isEmpty()) {
			Integer num = qu.poll();
			for(Node edge : city[num]) {
				if(!vis[edge.x]) {  //  避免重复遍历
					qu.add(edge.x);
					dis[edge.x] = dis[num] + edge.w;
					vis[edge.x] = true;
				}
			}
		}
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值