1. 程式人生 > >csp ccf 2017年12月份第4/四題 java實現 90分

csp ccf 2017年12月份第4/四題 java實現 90分

題目:

試題編號: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行描述道路,每行包含四個整數tabc,表示一條型別為t,連線ab兩個路口,長度為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 ≤ ab ≤ nt是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();//儲存最新階段的路徑,需要區分是大路還是小路 
	}
}