您的位置:首页 > 编程语言 > Java开发

csp ccf 2017年12月份第4/四题 java实现 90分

2018-02-22 23:19 417 查看
题目:

试题编号:201712-4
试题名称:行车路线
时间限制:1.0s
内存限制:256.0MB
问题描述:问题描述  小明和小芳出去乡村玩,小明负责开车,小芳来导航。
  小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
  例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=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,疲劳度为52=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 ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。保证答案不超过106。
考试时只得了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

;
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();//保存最新阶段的路径,需要区分是大路还是小路
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: