问题描述
某国有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 算法重做一遍。
如有错误,欢迎指正。