Skip to content

Commit bcbdf54

Browse files
authored
Merge pull request #9 from harry0000/scc
Add SccGraph
2 parents 5b7cafd + 1cb45a5 commit bcbdf54

File tree

4 files changed

+176
-6
lines changed

4 files changed

+176
-6
lines changed

src/main/scala/io/github/acl4s/Dsu.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,23 @@ case class Dsu(n: Int) {
5656
-parentOrSize(leader(a))
5757
}
5858

59-
def groups(): Seq[Seq[Int]] = {
59+
def groups(): collection.Seq[collection.Seq[Int]] = {
6060
val leader_buf = new Array[Int](n)
6161
val group_size = new Array[Int](n)
6262
(0 until n).foreach(i => {
6363
leader_buf(i) = leader(i)
6464
group_size(leader_buf(i)) += 1
6565
})
6666

67-
val result = new Array[mutable.ArrayBuffer[Int]](n)
67+
val result = new mutable.ArrayBuffer[mutable.Buffer[Int]](n)
6868
(0 until n).foreach(i => {
69-
result(i) = new mutable.ArrayBuffer(group_size(i))
69+
result += new mutable.ArrayBuffer(group_size(i))
7070
})
7171
(0 until n).foreach(i => {
7272
result(leader_buf(i)) += i
7373
})
7474

75-
result.collect {
76-
case buf if buf.nonEmpty => buf.toSeq
77-
}.toSeq
75+
result.filter(_.nonEmpty)
7876
}
7977

8078
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.github.acl4s
2+
3+
case class SccGraph(private val internal: io.github.acl4s.internal.SccGraph) {
4+
5+
def addEdge(from: Int, to: Int): Unit = {
6+
val n = internal.numVertices
7+
assert(0 <= from && from < n)
8+
assert(0 <= to && to < n)
9+
internal.addEdge(from, to)
10+
}
11+
12+
def scc(): collection.Seq[collection.Seq[Int]] = {
13+
internal.scc()
14+
}
15+
16+
}
17+
18+
object SccGraph {
19+
def apply(n: Int): SccGraph = {
20+
new SccGraph(internal.SccGraph(n))
21+
}
22+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package io.github.acl4s.internal
2+
3+
import scala.collection.mutable
4+
import scala.reflect.ClassTag
5+
6+
private[internal] case class Edge(to: Int)
7+
8+
private[internal] case class Csr[E] private (start: Array[Int], eList: Array[E])
9+
private[internal] object Csr {
10+
def apply[E: ClassTag](n: Int, edges: collection.Seq[(Int, E)]): Csr[E] = {
11+
val csr = Csr(new Array[Int](n + 1), new Array[E](edges.size))
12+
for ((from, _) <- edges) {
13+
csr.start(from + 1) += 1
14+
}
15+
(1 to n).foreach(i => {
16+
csr.start(i) += csr.start(i - 1)
17+
})
18+
val counter = csr.start.clone()
19+
for ((from, edge) <- edges) {
20+
csr.eList(counter(from)) = edge
21+
counter(from) += 1
22+
}
23+
csr
24+
}
25+
}
26+
27+
/**
28+
* Reference:
29+
* R. Tarjan,
30+
* Depth-First Search and Linear Graph Algorithms
31+
*/
32+
case class SccGraph(private val n: Int) {
33+
private val edges: mutable.Buffer[(Int, Edge)] = mutable.ListBuffer.empty
34+
35+
def numVertices: Int = n
36+
37+
def addEdge(from: Int, to: Int): Unit = {
38+
edges += ((from, Edge(to)))
39+
}
40+
41+
/**
42+
* @return pair of (# of scc, scc id)
43+
*/
44+
def sccIds(): (Int, Array[Int]) = {
45+
val g = Csr(n, edges)
46+
var now_ord = 0
47+
var group_num = 0
48+
val visited = new mutable.Stack[Int](n)
49+
val ord = Array.fill(n)(-1)
50+
val low = new Array[Int](n)
51+
val ids = new Array[Int](n)
52+
53+
def dfs(v: Int): Unit = {
54+
low(v) = now_ord
55+
ord(v) = now_ord
56+
now_ord += 1
57+
visited.push(v)
58+
(g.start(v) until g.start(v + 1)).foreach(i => {
59+
val to = g.eList(i).to
60+
if (ord(to) == -1) {
61+
dfs(to)
62+
low(v) = low(v).min(low(to))
63+
} else {
64+
low(v) = low(v).min(ord(to))
65+
}
66+
})
67+
if (low(v) == ord(v)) {
68+
while {
69+
// do
70+
val u = visited.pop()
71+
ord(u) = n
72+
ids(u) = group_num
73+
74+
// while
75+
u != v
76+
} do {}
77+
group_num += 1
78+
}
79+
}
80+
81+
(0 until n).foreach(i => {
82+
if (ord(i) == -1) { dfs(i) }
83+
})
84+
(0 until n).foreach(i => {
85+
ids(i) = group_num - 1 - ids(i)
86+
})
87+
88+
(group_num, ids)
89+
}
90+
91+
def scc(): collection.Seq[collection.Seq[Int]] = {
92+
val (group_nums, ids) = sccIds()
93+
val counts = new Array[Int](group_nums)
94+
ids.foreach(x => { counts(x) += 1 })
95+
val groups = new mutable.ArrayBuffer[mutable.Buffer[Int]](n)
96+
(0 until group_nums).foreach(i => {
97+
groups += new mutable.ArrayBuffer[Int](counts(i))
98+
})
99+
(0 until n).foreach(i => {
100+
groups(ids(i)) += i
101+
})
102+
103+
groups
104+
}
105+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.github.acl4s
2+
3+
class SccGraphSuite extends munit.FunSuite {
4+
5+
/**
6+
* @see https://atcoder.jp/contests/practice2/tasks/practice2_g
7+
*/
8+
test("AtCoder Library Practice Contest G - SCC") {
9+
val graph = SccGraph(6)
10+
graph.addEdge(1, 4)
11+
graph.addEdge(5, 2)
12+
graph.addEdge(3, 0)
13+
graph.addEdge(5, 5)
14+
graph.addEdge(4, 1)
15+
graph.addEdge(0, 3)
16+
graph.addEdge(4, 2)
17+
18+
val scc = graph.scc()
19+
assertEquals(scc.size, 4)
20+
assertEquals(scc.map(_.toSet).toSet, Set(Set(5), Set(1, 4), Set(2), Set(0, 3)))
21+
}
22+
23+
test("empty") {
24+
assertEquals(SccGraph(0).scc(), Seq())
25+
}
26+
27+
test("simple") {
28+
val graph = SccGraph(2)
29+
graph.addEdge(0, 1)
30+
graph.addEdge(1, 0)
31+
32+
val scc = graph.scc()
33+
assertEquals(scc.size, 1)
34+
}
35+
36+
test("self loop") {
37+
val graph = SccGraph(2)
38+
graph.addEdge(0, 0)
39+
graph.addEdge(0, 0)
40+
graph.addEdge(1, 1)
41+
42+
val scc = graph.scc()
43+
assertEquals(scc.size, 2)
44+
}
45+
}

0 commit comments

Comments
 (0)