CSP201509-4(高速公路)(Java 90分)
阿新 • • 發佈:2018-11-22
問題描述
某國有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不容易爆記憶體
關於這兩個演算法的詳細解釋請移步
我的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 演算法重做一遍。
如有錯誤,歡迎指正。