[BAEKJOON] 17616. 등수 찾기
Posted on
문제 :
KOI 본선 대회에 N명의 학생이 참가했다. 이 학생들을 각각 1부터 N까지 정수로 표현하자. 대회가 끝나고 성적을 발표하는데, 이 대회는 전체 학생의 등수를 발표 하는 대신, 두 학생 A, B가 대회 본부에 찾아가면 본부는 두 학생 중 어느 학생이 더 잘 했는지를 알려준다. 둘 이상의 학생이 동점인 경우는 없다.
자신의 전체에서 등수가 궁금한 학생들은 둘 씩 짝을 지어서 대회 본부에 총 M번 질문을 했다. 여러분은 등수를 알고 싶은 학생 X와 대회 본부의 질문에 대한 답들 로부터 이 학생 X의 등수 범위를 찾아서 출력한다. 질문에 대한 답으로 알 수 있는 최대한 정확한 답을 출력한다.
입력 :
표준 입력으로 다음 정보가 주어진다. 첫 번째 줄에 세 정수 N, M, X가 공백을 사이에 두고 주어진다. (2 ≤ N ≤ 105, 1 ≤ M ≤ min(N(N-1)/2, 5×105), 1 ≤ X ≤ N) . 다음 M 줄에는 각각 두 정수 A, B가 주어지는데, 이 뜻은 학생 A가 학생 B보다 더 잘했다는 뜻이다. 같은 A, B가 둘 이상의 줄에 주어지는 경우는 없고, 입력된 값이 정확함이 보장된다.
출력 :
표준 출력으로 두 정수 U, V (1 ≤ U ≤ V ≤ N)를 출력한다. 이는 학생 X의 가능한 가장 높은 등수가 U, 가능한 가장 낮은 등수가 V임을 나타낸다. 만약 학생 X의 가능한 등수가 정확하게 하나라면, U = V이다.
풀이 :
순위가 둘 중 어느 학생이 높은지에 관한 정보가 담긴 쌍이 여러 개 주어지는 것을 보고 위상정렬로 접근해야할 것이라고 예상했다.
하지만 해당 학생이 최소 몇 등부터, 최대 몇 등까지 가능한지 범위를 구해줘야 했기 때문에 단순한 위상정렬 알고리즘으로는 해결할 수 없을 것이라 판단했다. 그래서 위상정렬을 방향을 역으로 해서 한 번 더 실행해 총 두번의 위상정렬 탐색(bfs)으로 해답을 구할 수 있는 방법을 구상했다.
세부 구현사항 :
- 방향성을 가지는 topological sort adj 배열을 두개 준비한다. loweradj의 경우 자신보다 낮은 순위로 방향을 가지는 인접행렬을 구성하고, higheradj의 경우 자신보다 높은 순위로 방향을 가지는 인접행렬을 구성한다.
- 해당 학생의 최대 순위를 저장하는 highestrank, 최소 순위를 저장하는 lowestrank 변수를 준비해 각각 1, n 으로 초기화한다.
- 해당 학생보다 낮은 순위를 가지는 학생의 수를 loweradj 인접 벡터의 위상정렬 탐색을 통해 구한 후 lowestrank의 값에 빼준다.
- 해당 학생보다 높은 순위를 가지는 학생의 수는 higheradj 인접 벡터의 위상정렬 탐색을 통해 구한 후 highestrank의 값에 더해준다.
다음의 방식으로 위상정렬 탐색(bfs) 두 번 으로 순위 범위의 해답을 얻을 수 있다.
코드 :
코드 보기/접기
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<vector<int>> loweradj, higheradj;
int n, orgstu;
int bfs(bool isloweradj) {
vector<vector<int>> adj;
queue<int> q;
bool *used = new bool[n + 1]{false};
int count = 0;
q.push(orgstu);
used[orgstu] = true;
adj = (isloweradj) ? loweradj : higheradj;
while (!q.empty()) {
int curidx = q.front();
q.pop();
int size = adj[curidx].size();
for (int k = 0; k < size; k++) {
int cmpidx = adj[curidx][k];
if (used[cmpidx]) continue;
q.push(cmpidx);
used[cmpidx] = true;
count++;
}
}
return count;
}
int main() {
int m, fir, sec;
cin >> n >> m >> orgstu;
int highestrank = 1, lowestrank = n;
loweradj.resize(n + 1);
higheradj.resize(n + 1);
while (m--) {
cin >> fir >> sec;
loweradj[fir].push_back(sec);
higheradj[sec].push_back(fir);
}
lowestrank -= bfs(true);
highestrank += bfs(false);
cout << highestrank << ' ' << lowestrank << '\n';
return 0;
}