問題描述
小明和小芳出去鄉村玩,小明負責開車,小芳來導航。
小芳將可能的道路分為大道和小道。大道比較好走,每走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。
現在小芳拿到了地圖,請幫助她規划一個開車的路線,使得按這個路線開車小明的疲勞度最小。
小芳將可能的道路分為大道和小道。大道比較好走,每走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號路口是連通的。
接下來 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
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,當 t為0時 c ≤ 10 5,當 t為1時 c≤ 80。保證答案不超過10 6。
對於另外20%的評測用例,不存在小道;
對於另外20%的評測用例,所有的小道不相交;
對於所有評測用例,1 ≤ n ≤ 500,1 ≤ m ≤ 10 5,1 ≤ a, b ≤ n, t是0或1,當 t為0時 c ≤ 10 5,當 t為1時 c≤ 80。保證答案不超過10 6。
解題思路
使用鄰接表存儲邊,基於迪傑斯特拉算法計算最短的疲勞值。使用一個長度為n的數組記錄,某結點之前走的小道的長度。
在Dijstra中比較更新最短路時,如果迭代到小路時,比較路的長度為((選取結點的最短路)-選取結點之前走的小路的平方+(選取結點之前走的小路長度+本結點選擇的小路長度)的平方),如果比較路小於本結點當前最短路,則替換當前最短路,本結點之前走的小路長度記為(選取結點之前走的小路+本次走的小路長度),如果大於等於,不修改,繼續迭代;如果迭代到大路,比較路的長度為(選取結點的最短路+本結點選擇的大路),如果比較路小於本結點當前最短路,則替換當前最短路,且本結點之前走的最短路記為0,如果大於等於,不修改,繼續迭代。直至n結點確定最短路,程序結束,輸入n結點的最短路數值。
(有問題。。)
代碼實現
import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Scanner; public class Main { private static int n; private final static int MAX = Integer.MAX_VALUE; private static boolean[] finalVex; private static int[] shortPath; private static List<LinkedList<Edge>> list; public static void shortPathDij() { Edge tmp = null; shortPath = new int[n]; int[] tails = new int[n]; int[] exp = new int[n]; finalVex = new boolean[n]; Arrays.fill(shortPath, MAX); Arrays.fill(finalVex, false); Arrays.fill(exp, 0); shortPath[0] = 0; tails[0] = 1; while(!finalVex[n-1]) { int index = min(shortPath); if(index == -1) break; LinkedList<Edge> p = list.get(index); Iterator<Edge> it = p.iterator(); int j=0; while(it.hasNext()) { tmp = it.next(); j = tmp.end; if(finalVex[j]) continue; if(tmp.type==1) { int eee = exp[index]+tmp.weight; int sum = shortPath[index]-(int)Math.pow(exp[index], 2)+(int)Math.pow(eee, 2); if(sum<shortPath[j]) { shortPath[j] = sum; tails[j] = index+1; exp[j] = eee; } } else { if((shortPath[index]+tmp.weight)<shortPath[j]) { shortPath[j] = shortPath[index]+tmp.weight; tails[j] = index+1; exp[j] = 0; } } } } } private static int min(int[] arr) { int index = -1; for(int i=0; i<n; i++) if(!finalVex[i]) index = i; if(index==-1) return -1; for(int i=0; i<arr.length; i++) if(arr[index]>arr[i]&&!finalVex[i]) index = i; finalVex[index] = true; return index; } public static void main(String[] args) { Scanner in = new Scanner(System.in); n = in.nextInt(); int nums = in.nextInt(); list = new ArrayList<>(n); for(int i=0; i<n; i++) { list.add(new LinkedList<Edge>()); } for(int i=0; i<nums; i++) { int type = in.nextInt(); int start = in.nextInt(); int end = in.nextInt(); int weight = in.nextInt(); list.get(start-1).add(new Edge(type, start-1, end-1, weight)); list.get(end-1).add(new Edge(type, end-1, start-1, weight)); } shortPathDij(); System.out.println(shortPath[n-1]); in.close(); } } class Edge{ public int type; public int start; public int end; public int weight; public Edge(int type, int start, int end, int weight) { this.type = type; this.start = start; this.end = end; this.weight = weight; } }