蓝桥杯13年 大臣的旅费(java)

一、题目
时间限制:1.0s 内存限制:256.0MB

问题描述

很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J是T国重要大臣,他巡查于各大城市之间,体察民情。所以,从一个城市马不停蹄地到另一个城市成了J最常做的事情。他有一个钱袋,用于存放往来城市间的路费。

聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第x千米到第x+1千米这一千米中(x是整数),他花费的路费是x+10这么多。也就是说走1千米花费11,走2千米要花费23。

J大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?
输入格式

输入的第一行包含一个整数n,表示包括首都在内的T王国的城市数

城市从1开始依次编号,1号城市为首都。

接下来n-1行,描述T国的高速路(T国的高速路一定是n-1条)

每行三个整数Pi, Qi, Di,表示城市Pi和城市Qi之间有一条高速路,长度为Di千米。
输出格式

输出一个整数,表示大臣J最多花费的路费是多少。
样例输入1
5
1 2 2
1 3 1
2 4 5
2 5 4
样例输出1
135
输出格式

大臣J从城市4到城市5要花费135的路费。

二、循环+DFS(测评只有75分)
看到这个题的第一反应是dfs回溯,首先用一个矩阵map[i][j] 存储输入的地图信息,含义是从城市i+1到城市j+1的距离。然后定义一个访问标志数组isVIsited[I],等于1表示第i+1个城市已经走过。
每个城市都有可能是出发点,遍历每一个出发点,分别进行dfs,找到最长的一条路线,最后比较所有出发点的最长路线,选择其中最长的即为答案。
dfs函数中,边界的判定为当走到末路(没有其他节点可以走时),这样得到的是一条完整的路。

import java.util.Scanner;
//大臣的旅费
public class Main {
	static int[][] map;
	static int[] isVisited;
	static int km = 0;  //从任一出发点出发得到的最长路程
	static int km_final=0;  //总最长路程
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in= new Scanner(System.in);
		int n = in.nextInt();
		map = new int[n][n];  //map[i][j]表示从城市i+1到j+1的距离,0表示不通
		isVisited = new int[n];   //isVisited[i]表示城市i+1是否走过
		for(int i=0;i<n;i++)
		{
			isVisited[i]=0;   //初始化isVisited
			for(int j=0;j<n;j++)
				map[i][j]=0;  //初始化map
		}
		for(int i=0;i<n-1;i++)
		{
			int c1=in.nextInt();
			int c2=in.nextInt();
			int dis=in.nextInt();
			map[c1-1][c2-1]=dis;
			map[c2-1][c1-1]=dis;
		}
		for(int i=0;i<n;i++)  //遍历所有出发点,选择路程最大的
		{
			isVisited[i]=1;  //注意出发点一定要标记为走过
			dfs(i,n,0);
			km_final=km_final>km?km_final:km;   //选取最大的路程
			km=0;          //每次更换出发点前,更新km      
			isVisited[i]=0;    //更换出发点前,更新其访问状态 
		}
		System.out.print((int)(0.5*km_final*km_final+10.5*km_final));
	}
	private static void dfs(int a,int n,int dis)
	{
		if(isEnd(a,n))
		{
			km=km>dis?km:dis;
			return;
		}
		for(int i=0;i<n;i++)
		{
			if(map[a][i]!=0&&isVisited[i]==0)
			{
				isVisited[i]=1;
				dfs(i,n,dis+map[a][i]);
				isVisited[i]=0;
			}
		}
	}
	private static boolean isEnd(int a,int n)
	{
		for(int i=0;i<n;i++)
		{
			if(map[a][i]!=0&&isVisited[i]==0)
				return false;  //不是末路
		}
		return true;   
	}
}

评测结果:
在这里插入图片描述最后一组数据太大,结果运行超时了。然后在网上看到大家是用树的直径来求,进行两次神搜。

三、第二种方法(还是75分…)
树的直径定义:给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。
将王国地图看成一棵树,则题目需要找到的最长路径即是树的直径。

树的直径的求法:
通过两次DFS求。第一次:从任一节点出发,通过DFS求出最长的路径的终端节点。第二次:从上次求出的终端节点出发,通过DFS求出最长路径,即为数的直径。

import java.util.Scanner;

public class Main {
	static int[][] map;
	static int[] isVisited;
	static int km = 0;
	static int firstP = 0;
	public static void main(String[] args) {
		Scanner in= new Scanner(System.in);
		int n = in.nextInt();
		map = new int[n][n];  //map[i][j]表示从城市i+1到j+1的距离,0表示不通
		isVisited = new int[n];   //isVisited[i]表示城市i+1是否走过
		for(int i=0;i<n;i++)
		{
			isVisited[i]=0;   //初始化isVisited
			for(int j=0;j<n;j++)
				map[i][j]=0;  //初始化map
		}
		for(int i=0;i<n-1;i++)
		{
			int c1=in.nextInt();
			int c2=in.nextInt();
			int dis=in.nextInt();
			map[c1-1][c2-1]=dis;
			map[c2-1][c1-1]=dis;
		}
		isVisited[0]=1;
		dfs(0,n,0);
		isVisited[0]=0;
		//此时得到了从节点1出发的最长路径终点firstP
		isVisited[firstP]=1;
		dfs(firstP,n,0);
		isVisited[firstP]=0;
		//从firstP出发,找到最长路径就是树的直径
		System.out.print((int)(0.5*km*km+10.5*km));
	}
	private static void dfs(int a,int n,int dis)
	{
		if(isEnd(a,n))
		{
			if(km<dis)
			{
				km=dis;
				firstP=a;
			}
			return;
		}
		for(int i=0;i<n;i++)
		{
			if(map[a][i]!=0&&isVisited[i]==0)
			{
				isVisited[i]=1;
				dfs(i,n,dis+map[a][i]);
				isVisited[i]=0;
			}
		}
	}
	private static boolean isEnd(int a,int n)
	{
		for(int i=0;i<n;i++)
		{
			if(map[a][i]!=0&&isVisited[i]==0)
				return false;  //不是末路
		}
		return true;   
	}
}


评测结果: 最后一组数据仍然无法通过
在这里插入图片描述
四、蓝桥杯老师给的方法
还是用树的直径来算,改进的地方是:利用邻接表来存储王国地图,最后能够100分通过。

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class Node {
	int num;
	long dis;
	public Node(int num,long dis)
	{
		this.num=num;
		this.dis=dis;
	}
}
public class Main {
	static int n;
	static long max=-1;
	static int endp=-1;
	static List<Node>[] gf;  //邻接表
	public static void main(String[] args) {
		Scanner in= new Scanner(System.in);
		n = in.nextInt();
		gf = new List[n];  //初始化
		for(int i=0;i<n;i++)
		{
			gf[i]=new ArrayList<Node>();
		}
		for(int i=0;i<n-1;i++)
		{
			int c1=in.nextInt();
			int c2=in.nextInt();
			long dis=in.nextLong();
			gf[c1-1].add(new Node(c2-1,dis));
			gf[c2-1].add(new Node(c1-1,dis));
		}
		dfs(0,0,0);
		dfs(endp,endp,0);
		System.out.println((11 *max + max * (max - 1) / 2));
		
	}
	private static void dfs(int from,int now,long dis)
	{
		boolean isLeaf = true;
		List<Node> neighbors = gf[now];
		for(int i=0;i<neighbors.size();i++)
		{
			Node nei = neighbors.get(i);
			if(nei.num==from)   
				continue;
			isLeaf=false;
			dfs(now,nei.num,dis+nei.dis);
		}
		if(isLeaf&&dis>max)
		{
			max=dis;
			endp=now;
		}	
	}
}


在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值