1. 程式人生 > >CSP201509-4(高速公路)(Java 90分)

CSP201509-4(高速公路)(Java 90分)

問題描述
  某國有n個城市,為了使得城市間的交通更便利,該國國王打算在城市之間修一些高速公路,由於經費限制,國王打算第一階段先在部分城市之間修一些單向的高速公路。
  現在,大臣們幫國王擬了一個修高速公路的計劃。看了計劃後,國王發現,有些城市之間可以通過高速公路直接(不經過其他城市)或間接(經過一個或多個其他城市)到達,而有的卻不能。如果城市A可以通過高速公路到達城市B,而且城市B也可以通過高速公路到達城市A,則這兩個城市被稱為便利城市對。
  國王想知道,在大臣們給他的計劃中,有多少個便利城市對。
輸入格式
  輸入的第一行包含兩個整數n, m,分別表示城市和單向高速公路的數量。
  接下來m行,每行兩個整數a, b,表示城市a有一條單向的高速公路連向城市b。
輸出格式


  輸出一行,包含一個整數,表示便利城市對的數量。
樣例輸入
5 5
1 2
2 3
3 4
4 2
3 5
樣例輸出
3
樣例說明
在這裡插入圖片描述
思路:這個題題意很明顯,就是求強連通分量,對於任意一個頂點數大於等於2的 強連通分量,假設強連通分量內的頂點數為n,那麼該強連通分量共有n*(n-1)/2個遍歷城市對。所以重點就是求強連通分量。我用了Kosaraju演算法。
求強連通分量的有名演算法一共有兩個 Tarjan 和 Kosaraju。
Kosaraju 容易理解,但演算法時間複雜度較高,容易爆記憶體
Tarjan 難理解,但是演算法時間複雜度低,相比於 Kosaraju不容易爆記憶體
關於這兩個演算法的詳細解釋請移步
https://www.cnblogs.com/reddest/p/5932153.html

我的Java程式碼90分,原因是爆記憶體了。
這是我的提交記錄在這裡插入圖片描述
第一次提交時得了90分,提示執行錯誤
然後我找原因,沒找到,繼續提交,一模一樣的程式碼居然變成80分了。
我又試了一次還是80分。。
然後我把

G[i]=new ArrayList<>();
rG[i]=new ArrayList<>();

改成了

G[i]=new ArrayList<>(n);
rG[i]=new ArrayList<>(n);

提交以後變成0分了。。
所以總結出來就是爆記憶體了。。

接下來貼我的程式碼:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class D {
	static int V;//頂點數
	static List<Integer> G[];//圖的鄰接表示
	static List<Integer> rG[];//把邊反向後的圖
	static List<Integer> vs;//後序遍歷的頂點序列
	static boolean used[];//訪問標記
	static int cmp[];//所屬強連通分量的拓撲序
	static Map<Integer, Integer> map;//每個強聯通分量的頂點個數
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m =sc.nextInt();
		V = n;
		G =new ArrayList[n];
		rG = new ArrayList[n];
		for(int i=0;i<n;i++) {
			G[i]=new ArrayList<>();
			rG[i]=new ArrayList<>();
		}
		vs =new ArrayList<>();
		used  = new boolean[n];
		cmp = new int[n];
		map = new HashMap<Integer, Integer>();
		
		for(int i=0;i<m;i++) {
			add_edge(sc.nextInt()-1, sc.nextInt()-1);
		}
		int tmp = scc();
		int result=0;
		for(int i=0;i<tmp;i++) {
			int t1 = map.get(i);
			if(t1>=2) {
				int t2=t1*(t1-1)/2;
				result+=t2;
			}
		}
		System.out.println(result);
		
	}
	static void add_edge(int from,int to) {
		G[from].add(to);
		rG[to].add(from);
	}
	static void dfs(int v) {
		used[v] = true;
		for(int i=0;i<G[v].size();i++) {
			if(!used[G[v].get(i)]) {
				dfs(G[v].get(i));
			}
		}
		vs.add(v);
	}
	static void rdfs(int v,int k) {
		used[v] = true;
		
		cmp[v]=k;
		for(int i=0;i<rG[v].size();i++) {
			if(!used[rG[v].get(i)]) {
				rdfs(rG[v].get(i),k);
			}
		}
	}
	public static int scc() {
		for(int i=0;i<used.length;i++) {
			used[i]=false;
		}
		for(int i=0;i<V;i++) {
			if(!used[i]) {
				dfs(i);
			}
		}
		for(int i=0;i<used.length;i++) {
			used[i]=false;
		}
		int k=0;
		for(int i=vs.size()-1;i>=0;i--) {
			if(!used[vs.get(i)]) {
				rdfs(vs.get(i),k);
				k++;
			}
		}
		for(int i=0;i<V;i++) {
			if(map.containsKey(cmp[i])) {
				map.put(cmp[i], map.get(cmp[i])+1);
			}
			else {
				map.put(cmp[i],1);
			}
		}
		return k;
	}
}

等有時間我會嘗試用Tarjan 演算法重做一遍。
如有錯誤,歡迎指正。