1. 문제
1번부터 n번까지 번호가 붙여진 학생들의 키 순서가 주어진다.
예를 들어 1번 < 5번, 3번 < 4번, 5번 < 4번,....
이 결과로 모든 학생중 키가 가장 작은 학생부터 자신이 몇번째 학생인지 알 수 있는 학생도 있고,
그렇지 못한 학생들도 있다.
이러한 결과가 주어질때 자신의 키가 몇번째 학생인지 정확히 알 수 있는 학생 수를 구한다
------------------------------------------------------------------------------------------------------------------------------------------------
상식적으로? 먼저 생각해보면
i번이 몇번째 순서인지 알고 싶다면 i번보다 키가 큰 학생의 수, 키가 작은 학생의 수를 찾으면 알 수 있다
1번 < 5번
5번 < 4번
5번 < 2번
3번 < 4번
4번 < 6번
4번 < 2번
4번보다 키가 작은 학생은 1번, 3번, 5번
4번보다 키가 큰 학생은 2번, 6번
따라서 4번 학생은 위로 2명 있고 아래로 3명 있으므로 3등임을 정확히 알 수 있다
하지만 1번 학생보다 키가 큰 학생은 5번, 4번, 2번, 6번
그치만 1번과 3번 학생 사이를 비교할 수 없기 때문에 1번이 정확히 1등인지 2등인지는 알 수 없다
그래서 n명 있을때 자기보다 키가 큰 학생의 수 + 자기보다 키가 작은 학생의 수 = n-1이면 그 사람의 등수는 정확히 알 수 있다
그러면 키 관계가 주어질때 그래프로 만들고 i번에서 도달 가능한 정점을 모두 찾는다
a,b가 주어지면 a 에서 b로 갈 수 있다는 뜻이고 키 관계는 a < b라는 뜻
처음에 생각한건 n <= 500이라 무난하게 플로이드 워셜
dp[a][b] = 1로 두고, 모든 정점을 돌아 dp[a][b]값을 갱신하고,
모든 i = 1,2,..,n에 대하여 dp[i][j] >= 1이면 i에서 j로 갈 수 있다는 뜻이고, i보다 키가 큰 학생이 j라는 뜻
dp[j][i] >= 1이면 j에서 i로 갈수 있다는 뜻인데 i보다 키가 작은 학생이 j라는 뜻이 될 것
두 학생의 수가 n-1이면 i번은 키가 몇번째인지 알 수 있게 된다
from sys import stdin
n,m = map(int,stdin.readline().split())
INF = 10**9
dp = [[INF]*(n+1) for _ in range(n+1)]
for _ in range(m):
a,b = map(int,stdin.readline().split())
#a < b
dp[a][b] = 1
for i in range(1,n+1):
dp[i][i] = 0
for k in range(1,n+1):
for a in range(1,n+1):
for b in range(1,n+1):
if dp[a][k] + dp[k][b] < dp[a][b]:
dp[a][b] = dp[a][k] + dp[k][b]
count = 0
for i in range(1,n+1):
big = 0
small = 0
for j in range(1,n+1):
if dp[i][j] != INF and dp[i][j] >= 1:
big += 1
if dp[j][i] != INF and dp[j][i] >= 1:
small += 1
if big + small == n-1:
count += 1
print(count)
이 방법은 n이 커지면 힘들다
그래서 a에서 b로 갈 수 있는 간선을 가진 그래프 g 하나랑,
b에서 a로 갈 수 있는 역방향 간선을 가진 그래프 r_g 하나를 하나 더 만들고
두 그래프 각각에서 i번에서 도달 가능한 정점의 수를 찾는다
g에서 도달 가능한 정점들은 a보다 키가 큰 학생이 될거고 r_g에서 도달 가능한 정점들은 a보다 키가 작은 학생들
도달 가능한 정점 수의 합이 n-1이면 i번은 등수를 정확히 알 수 있다
모든 i = 1,2,..,n에 대해 각각 dfs 2번씩만 하면 각 정점이 몇등인지 정확히 알 수 있는 정점들을 찾을 수 있다
from sys import stdin
def dfs(u,g,visited):
for v in g[u]:
if visited[v] == 0:
visited[v] = 1
dfs(v,g,visited)
n,m = map(int,stdin.readline().split())
g = [[] for _ in range(n+1)]
r_g = [[] for _ in range(n+1)]
for _ in range(m):
a,b = map(int,stdin.readline().split())
#a < b
g[a].append(b)
r_g[b].append(a)
count = 0
for i in range(1,n+1):
visited = [0]*(n+1)
visited[i] = 1
dfs(i,g,visited)
x = sum(visited)-1
visited = [0]*(n+1)
visited[i] = 1
dfs(i,r_g,visited)
y = sum(visited)-1
if x+y == n-1:
count += 1
print(count)
visited는 i부터 시작해서 방문한 정점들을 체크한 배열이니까
visited[v] = 1
dfs(v)
하고 나서 visited[v] = 0으로 복구해버리면 안되겠지
----------------------------------------------------------------------------------------------------------------------------------------------------------------
2. 비슷한 문제
순서 관계가 주어지는 똑같은 문제인데 이번에는 x번 학생에 대해 이 학생이 몇등부터 몇등까지 가능한지를 찾는 문제
이번엔 n <= 10^5라서 dfs로 접근해야한다
그냥 간선을 가진 그래프를 놓고, 역방향 간선을 가진 그래프를 추가로 둔다
그러면 각각에 대해서 dfs를 수행하면 도달 가능한 정점을 찾을 수 있다
이때 x번 학생에 대해 더 큰 노드를 가진 학생 수가 a명
x번 학생에 대해 더 작은 노드를 가진 학생 수가 b명
그러면 x번의 순위 범위는?
예를 들어 총 7명 있다고 할때
1,2,3,4,5,6,7
x번의 위로 1명 도달 가능하고 아래로 3명 도달 가능하다고 한다면?
2등,3등,4등 중 하나 가능하다
위로 2명 도달 가능하고 아래로 2명 도달 가능하다고 한다면?
3등,4등,5등 가능하다
위로 a명 도달 가능하고, 아래로 b명 도달 가능하다고 한다면?
1 + a등부터 n - b등까지 가능하게 된다
from sys import stdin
def dfs(u,edge):
for v in edge[u]:
if visited[v] == 0:
visited[v] = 1
dfs(v,edge)
n,m,x = map(int,stdin.readline().split())
edge = [[] for _ in range(n+1)]
r_edge = [[] for _ in range(n+1)]
for _ in range(m):
a,b = map(int,stdin.readline().split())
edge[a].append(b)
r_edge[b].append(a)
visited = [0]*(n+1)
visited[x] = 1
dfs(x,edge)
win = sum(visited) - 1
visited = [0]*(n+1)
visited[x] = 1
dfs(x,r_edge)
lose = sum(visited) - 1
print(1 + lose, n - win)
'알고리즘 > 그래프 이론 정복기' 카테고리의 다른 글
DFS로 사이클 탐지하는 알고리즘 탐구하기2 -사이클의 길이- (0) | 2024.06.15 |
---|---|
DFS로 사이클 탐지하는 알고리즘 탐구하기1 -기본 이론- (0) | 2024.06.14 |
DFS 2번으로 트리의 지름을 구하는 방법 (0) | 2023.11.28 |
저울의 무게 비교를 그래프로 바꿔서 최단 거리 알고리즘으로 해결(플로이드 워셜, 그래프 탐색) (0) | 2023.09.07 |
강한 연결 요소(Strongly connected component)를 구하는 코사라주 알고리즘(kosaraju's algorithm) 배우기 (0) | 2023.09.02 |