题目:
试题编号: | 201712-4 |
试题名称: | 行车路线 |
时间限制: | 1.0s |
内存限制: | 256.0MB |
问题描述: |
问题描述
小明和小芳出去乡村玩,小明负责开车,小芳来导航。
小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走 s公里小明会增加 s 2的疲劳度。 例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2) 2+2+2 2=16+2+4=22。 现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
输入格式
输入的第一行包含两个整数
n,
m,分别表示路口的数量和道路的数量。路口由1至
n编号,小明需要开车从1号路口到
n号路口。
接下来 m行描述道路,每行包含四个整数 t, a, b, c,表示一条类型为 t,连接 a与 b两个路口,长度为 c公里的双向道路。其中 t为0表示大道, t为1表示小道。保证1号路口和 n号路口是连通的。
输出格式
输出一个整数,表示最优路线下小明的疲劳度。
样例输入
6 7
1 1 2 3 1 2 3 2 0 1 3 30 0 3 4 20 0 4 5 30 1 3 5 6 1 5 6 1
样例输出
76
样例说明
从1走小道到2,再走小道到3,疲劳度为5
2=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
数据规模和约定
对于30%的评测用例,1 ≤
n ≤ 8,1 ≤
m ≤ 10;
对于另外20%的评测用例,不存在小道; 对于另外20%的评测用例,所有的小道不相交; 对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 10 5,1 ≤ a, b ≤ n, t是0或1, c ≤ 10 5。保证答案不超过10 6。 |
考试时只得了60分,后来做了下得了80分:
思路:对于迪杰斯特拉算法进行修改,在保存从起点到各个终点的dist中(dist会被不断更新),一个终点对应一个key,而value不仅仅保存路径的值,还保存当前路径的最后一段路径的信息,如果该路段是连续的一段小道,将对后面产生影响,假设当前到终点B的需要更新,当前最短路径的到终点A已经选出来,需要从A出发到B构成新路径 ,起点-x-x-A-B,(x-x代表未知的一段)来试探是否新路径更短,如果此时A-B也是小道,而之前起点到A的最后一段路径也是连续的小道,设为(C-D-A),cd之间为L1,Da之间为L2,它们都是小道,A-B之间也是小道设为L3,连续的小道应该算上A-B,为(L1+L2+L3)的平方,我们已经知道(L1+L2)的平方(说明:这保存在之前说的数据结构中),(L1+L2+L3)的平方 = ((L1+L2)的开方+L3)的平方,这只是其中一种特殊的情况,通过这样不断的更新dist,最终求得答案
代码如下(可以直接提交):只得了80分,目前还不知道原因,不知道有没有100分的大佬讲讲其他思路
代码有很多优化的地方,只是为了验证正确性,所以没多去修改
更新:看了别人的博客说int类型会爆,于是修改了下,然后90分,其他地方没怎么修改
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
Dij.Node[][] martix = new Dij.Node[n][n];
for(int i= 0;i<m;i++) {
int t = scanner.nextInt();
int a = scanner.nextInt();
int b = scanner.nextInt();
int c = scanner.nextInt();
martix[a-1][b-1] = new Dij.Node(c, t==0?true:false);
martix[b-1][a-1] = new Dij.Node(c, t==0?true:false);
}
Dij dij = new Dij(martix, n);
dij.dij(0);
dij.output();
}
}
/**
* 核心算法实现类
* @author colin
*/
class Dij {
private int vertexNum;//顶点数量
private Node[][] martix;//邻接矩阵
private boolean[] U;//候选集合点,还没有找到最短路径的点
private Map<Integer,Path> dist = new HashMap<>();//保存最短路径key代表终点 value代表当前的最短路径
private int currentMidVertex;//当前的中间点,也就是每次产生的最短路径的那个点,该点之前不再S集合中
private int startVertex = 0;
public Dij(Node[][] martix,int vertexNum) {
this.martix = martix;
this.vertexNum = vertexNum;
U = new boolean[vertexNum];
}
private void init(int startVertex) {
//初始化U集合和dist集合
for(int i = 0; i<vertexNum; i++) {
U[i] = true;
if(i!=startVertex) {
if(martix[startVertex][i]!=null) {
Path path = new Path();
if(!martix[startVertex][i].isWideRoad) {
path.totalDistance = martix[startVertex][i].distance * martix[startVertex][i].distance;
path.lastStagePath.distance = path.totalDistance;
path.lastStagePath.isWideRoad = false;
} else {
path.totalDistance = martix[startVertex][i].distance;
path.lastStagePath.distance = martix[startVertex][i].distance;
path.lastStagePath.isWideRoad = true;
}
dist.put(i, path);
} else {
Path path = new Path();
path.totalDistance = Long.MAX_VALUE;
dist.put(i, path);
}
}
}
U[startVertex] = false;
//初始化中间点
this.currentMidVertex = startVertex;
}
public void dij(int startVertex) {//从哪个顶点出发
this.startVertex = startVertex;
init(startVertex);
//顶点个数-1次循环,每次从U集合中取出一个点
for(int i = 1;i<vertexNum;i++) {//循环顶点个数-1那么多次,每次加入一个顶点到S集合中
currentMidVertex = shorestPath();//找到dist中当前最短路径的终点是哪一个
if(currentMidVertex==Long.MAX_VALUE) {//如果出现v1-v2没有路径,那么从U集合中能拿出来的点都拿出来了
continue;
}
U[currentMidVertex] = false;//从候选集合中拿出去
//更新dist,使得其他路径经过currentMidVertex,如果路径值更小那么更新
Set<Entry<Integer,Path>> set = dist.entrySet();
for (Entry<Integer, Path> entry : set) {
if(entry.getKey()!=currentMidVertex&&U[entry.getKey()]) {
Path first = dist.get(currentMidVertex);
//新的路径就等于比如:当前的最短路径的终点是v3,若起点是v1,终点是v6,那么新的路径就是要找到是否可以经过v3从v1到v6,而只需要检测是否v3可以达到v6,如果可以那么新的路径就为v1->XXX->v3->v6
Node second = path(currentMidVertex,entry.getKey());
Path wait2update = entry.getValue();
//第二段没路可走
if(second==null) {
continue;
}
//如果后一段是小路
long result = Long.MAX_VALUE;
if(!second.isWideRoad) {
//如果前一段路也是小路
if(!first.lastStagePath.isWideRoad) {
long a = first.lastStagePath.distance;
//新构成的路径长度
result = (int) (first.totalDistance +2* Math.sqrt(a)*second.distance+ second.distance * second.distance);
if(result < wait2update.totalDistance) {
wait2update.totalDistance = result;
wait2update.lastStagePath.distance = (int) (a+2* Math.sqrt(a)*second.distance+ second.distance * second.distance);
wait2update.lastStagePath.isWideRoad = false;
}
//前一段路是大路
} else {
result = first.totalDistance+second.distance * second.distance;
if(result < wait2update.totalDistance) {
wait2update.totalDistance = result;
wait2update.lastStagePath.distance = second.distance * second.distance;
wait2update.lastStagePath.isWideRoad = false;
}
}
//后一段路是大路
} else {
result = first.totalDistance + second.distance;
if(result < wait2update.totalDistance) {
wait2update.totalDistance = result;
wait2update.lastStagePath.distance = second.distance;
wait2update.lastStagePath.isWideRoad = true;
}
}
}
}
}
}
public void output() {
System.out.println(dist.get(vertexNum-1).totalDistance);
}
private Node path(int start, Integer destination) {//寻找一个路径起点是start,终点是destination
if(martix[start][destination]!=null) {
return martix[start][destination];
}
return null;
}
//遍历dist返回当前的最短路径的终点是哪个
public int shorestPath() {
Set<Entry<Integer,Path>> set = dist.entrySet();
int minKey = Integer.MAX_VALUE;
long minPath = Long.MAX_VALUE;
for (Entry<Integer, Path> entry : set) {
if(U[entry.getKey()]) {
int key = entry.getKey();
long value = entry.getValue().totalDistance;
if(value < minPath) {
minPath = value;
minKey = key;
}
}
}
return minKey;
}
/**
*邻接矩阵元素数据结构
*/
public static class Node{
public long distance = Long.MAX_VALUE;//
public boolean isWideRoad = true;
public Node(int distance,boolean isWideRoad) {
this.distance = distance;
this.isWideRoad = isWideRoad;
}
public Node() {
}
}
/**
*描述到某个终点的最短距离
*需要不断更新
*/
public static class Path{
public long totalDistance = Long.MAX_VALUE;//保存距离的总和
public Node lastStagePath = new Node();//保存最新阶段的路径,需要区分是大路还是小路
}
}