一、题目
时间限制: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;
}
}
}