csp ccf 2017年12月份第4/四題 java實現 90分
阿新 • • 發佈:2019-01-03
題目:
試題編號: | 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行描述道路,每行包含四個整數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 對於另外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[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();//儲存最新階段的路徑,需要區分是大路還是小路
}
}