[문제]
올해 ACM-ICPC 대전 인터넷 예선에는 총 n개의 팀이 참가했다. 팀은 1번부터 n번까지 번호가 매겨져 있다. 놀랍게도 올해 참가하는 팀은 작년에 참가했던 팀과 동일하다.
올해는 인터넷 예선 본부에서는 최종 순위를 발표하지 않기로 했다. 그 대신에 작년에 비해서 상대적인 순위가 바뀐 팀의 목록만 발표하려고 한다. (작년에는 순위를 발표했다) 예를 들어, 작년에 팀 13이 팀 6 보다 순위가 높았는데, 올해 팀 6이 팀 13보다 순위가 높다면, (6, 13)을 발표할 것이다.
창영이는 이 정보만을 가지고 올해 최종 순위를 만들어보려고 한다. 작년 순위와 상대적인 순위가 바뀐 모든 팀의 목록이 주어졌을 때, 올해 순위를 만드는 프로그램을 작성하시오. 하지만, 본부에서 발표한 정보를 가지고 확실한 올해 순위를 만들 수 없는 경우가 있을 수도 있고, 일관성이 없는 잘못된 정보일 수도 있다. 이 두 경우도 모두 찾아내야 한다.
[입력 조건]
첫째 줄에는 테스트 케이스의 개수가 주어진다. 테스트 케이스는 100개를 넘지 않는다. 각 테스트 케이스는 다음과 같이 이루어져 있다.
- 팀의 수 n을 포함하고 있는 한 줄. (2 ≤ n ≤ 500)
- n개의 정수 ti를 포함하고 있는 한 줄. (1 ≤ ti ≤ n) ti는 작년에 i등을 한 팀의 번호이다. 1등이 가장 성적이 높은 팀이다. 모든 ti는 서로 다르다.
- 상대적인 등수가 바뀐 쌍의 수 m (0 ≤ m ≤ 25000)
- 두 정수 ai와 bi를 포함하고 있는 m줄. (1 ≤ ai < bi ≤ n) 상대적인 등수가 바뀐 두 팀이 주어진다. 같은 쌍이 여러 번 발표되는 경우는 없다.
[출력 조건]
각 테스트 케이스에 대해서 다음을 출력한다.
- n개의 정수를 한 줄에 출력한다. 출력하는 숫자는 올해 순위이며, 1등팀부터 순서대로 출력한다. 만약, 확실한 순위를 찾을 수 없다면 "?"를 출력한다. 데이터에 일관성이 없어서 순위를 정할 수 없는 경우에는 "IMPOSSIBLE"을 출력한다.
해결한 방법
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.*
fun main(){
val br = BufferedReader(InputStreamReader(System.`in`))
var stk : StringTokenizer
val tc = br.readLine().toInt()
repeat(tc){
val n = br.readLine().toInt() // 총 팀의 갯수
val graph = Array(n + 1){ mutableListOf<Int>() } // 순위 그래표
val degree = IntArray(n + 1) // 진입 차수
val teamArray = IntArray(n + 1) // 이전 순위 배열, index = 순위, value = 팀
stk = StringTokenizer(br.readLine())
for(i in 1 .. n){
val a = stk.nextToken().toInt()
teamArray[i] = a
for(j in 2 .. i){
graph[a].add(teamArray[j - 1]) // 높은 순위에 낮은 순위를 연결
degree[teamArray[j - 1]] += 1
}
}
val m = br.readLine().toInt() // 상대 순위 변경 팀의 갯수
repeat(m){
stk = StringTokenizer(br.readLine())
val a = stk.nextToken().toInt()
val b = stk.nextToken().toInt()
val indexA = teamArray.indexOf(a) // 이전 순위
val indexB = teamArray.indexOf(b) // 이전 순위
if(indexA > indexB) {
graph[a].remove(b)
degree[b] -= 1
graph[b].add(a)
degree[a] += 1
}
else {
graph[b].remove(a)
degree[a] -= 1
graph[a].add(b)
degree[b] += 1
}
}
val q : Queue<Int> = LinkedList()
for(i in 1 .. n){
if(degree[i] == 0){
q.add(i)
}
}
// impossible의 경우 -> cycle이 발생한 경우
// ?의 경우 -> 정점이면서 진입 차수가 0인 수가 2개인 경우 -> 여러개의 위상정렬이 가능한 경우
val result = mutableListOf<Int>()
while(q.isNotEmpty()) {
if(q.size > 1){
println("?")
return
}
val cur = q.poll()
result.add(cur)
for (i in graph[cur]) {
degree[i] -= 1
if (degree[i] == 0) {
q.add(i)
}
}
}
if(result.size == n){
for(i in (n - 1) downTo 0){
print("${result[i]} ")
}
}else{
print("IMPOSSIBLE")
}
println()
}
}
- 순위에 따라서 입력이 주어진다. graph 배열을 만들어 높은 인덱스(낮은 순위)는 모두 낮은 인덱스(높은 순위)를 가리키도록 구현했다. 진입 차수 배열을 따로 만들어 현재 인덱스를 가리키고 있는 수를 확인할 수 있도록 구성했다.
- 순위의 상대적인 변경이 일어난 경우 낮은 순위가 높은 순위보다 높은 순위가 되었다고 할 때, graph 배열에서 낮은 순위가 높은 순위를 가리키던 것을 제거하고 높은 순위가 낮은 순위를 가리키도록 수정한다. 또한 가리키는 갯수가 달라졌으니 진입 차수 역시 수정한다.
- 출력의 경우 세가지, 순위를 출력하거나 IMPOSSIBLE, ?을 출력할 수 있는데, IMPOSSIBLE의 경우는 사이클이 발생하였을 때를 의미하며(5등이 1등을, 1등이 다시 5등을 가리키는 상황) ?의 경우는 순위가 여러개가 생기는 경우의 수를 의미한다. 이는 정점인 동시에 진입 차수가 0인 노드가 2개 이상일 때 발생한다.