https://www.acmicpc.net/problem/18869
18869번: 멀티버스 Ⅱ
M개의 우주가 있고, 각 우주에는 1부터 N까지 번호가 매겨진 행성이 N개 있다. 행성의 크기를 알고 있을때, 균등한 우주의 쌍이 몇 개인지 구해보려고 한다. 구성이 같은데 순서만 다른 우주의 쌍
www.acmicpc.net
멀티버스 2 문제의 해결 방법은 2가지가 있습니다.
정렬을 이용한 방법
먼저, 첫번째 방법은 아래와 같습니다.
문제에서는 아래와 같은 조건을 만족하면, 두 우주가 균등하다고 합니다.
- Ai < Aj → Bi < Bj
- Ai = Aj → Bi = Bj
- Ai > Aj → Bi > Bj
따라서 대소 관계가 동일하면 두 우주가 균등하다고 할 수 있습니다.
먼저, 두 우주 A, B를 정렬하고, 정렬 후의 우주를 각각 X, Y라고 하겠습니다.
그럼 두 우주가 균등하다면, X, Y의 같은 인덱스의 요소는 정렬 전의 위치가 같아야합니다.
예를 들면, A = {1, 3, 2}이고, B = {12, 50, 31}이면, 정렬 후, X = {1, 2, 3}이고, Y = {12, 31, 50}입니다.
- X[0] = 1의 정렬 전 위치는 0이고, Y[0] = 12의 정렬 전 위치는 0입니다.
- X[1] = 2의 정렬 전 위치는 2이고, Y[1] = 31의 정렬 전 위치는 2입니다.
- X[2] = 3의 정렬 전 위치는 1이고, Y[2] = 50의 정렬 전 위치는 1입니다.
따라서 두 우주는 균등하다고 할 수 있습니다.
하지만, 아래의 테스트 케이스의 경우 위의 solution이 동작하지 않습니다.
2 5 1 1 1 2 3 1 1 1 1 2
이미 정렬된 두 우주에 대해서는 항상 균등하다고 판단하기 때문입니다.
따라서 (X[i] < X[i + 1]) == (Y[i] < Y[i + 1])을 만족하는지 추가해줘야합니다. (단, X, Y는 오름차순 정렬된 상태)
아래는 정렬을 이용한 전체 코드입니다.
#include <bits/stdc++.h> using namespace std; #define size first #define idx second pair<int, int> u[101][10010]; int main(){ ios::sync_with_stdio(0); cin.tie(0); int m, n; cin >> m >> n; for(int i = 0; i < m; i++){ for(int j = 0, size; j < n; j++){ cin >> size; u[i][j] = {size, j}; // {행성의 크기, 정렬 전 인덱스} } sort(&u[i][0], &u[i][n]); // 각 우주를 행성 크기를 기준으로 정렬 } int ans = 0; // 균등한 우주 쌍의 개수 for(int i = 0; i < m - 1; i++){ for(int j = i + 1; j < m; j++){ // 두 우주를 선택 (Combination) bool equal = true; // 두 우주가 균등한지 나타내는 Flag 변수 for(int k = 0; k < n; k++){ if(u[i][k].idx != u[j][k].idx) { // 정렬 전 위치가 같지 않다면 equal = false; // 두 우주는 균등하지 않은 우주 break; }else{ // 정렬 전 위치가 같다면 if(i != n - 1){ if(u[i][k].size < u[i][k + 1].size != u[j][k].size < u[j][k + 1].size){ // 대소관계 비교 equal = false; break; } } } } if(equal) ans++; } } cout << ans << "\n"; }
좌표 압축을 이용한 방법
좌표 압축은 수의 값에 무관하게 좌표 사이의 대소만 알면 될 때, 좌표 압축을 이용하여 수의 범위를 줄여줄 수 있습니다.
따라서, 두 우주가 균등하다면 좌표 압축 결과가 같습니다.
좌표 압축은
수직선 위에 N개의 좌표 X1, X2, ..., XN이 있을 때,
Xi를 좌표 압축한 결과 X'i의 값은 Xi > Xj를 만족하는 서로 다른 좌표의 개수입니다.
예를 들어 A = {1000, 999, 1000, 999, 1000, 999}는 좌표 압축 결과 A = {1, 0, 1, 0, 1, 0}이 됩니다.
아래는 좌표 압축을 이용한 전체 코드입니다.
#include <bits/stdc++.h> using namespace std; vector<vector<int>> u(101, vector<int>(0)); void compression(vector<int>& v){ // 벡터에 대해 좌표 압축을 수행하는 함수 vector<int> tmp(v.size()); copy(v.begin(), v.end(), tmp.begin()); // v를 tmp 벡터에 copy sort(tmp.begin(), tmp.end()); // tmp 벡터를 오름차순 정렬 tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); // 중복 제거 for(int i = 0; i < v.size(); i++){ // X'i 계산 int idx = lower_bound(tmp.begin(), tmp.end(), v[i]) - tmp.begin(); v[i] = idx; } } int main(){ ios::sync_with_stdio(0); cin.tie(0); int m, n; cin >> m >> n; for(int i = 0; i < m; i++){ for(int j = 0, size; j < n; j++){ cin >> size; u[i].push_back(size); } compression(u[i]); // 각각의 우주에 대해 좌표 압축 수행 } int ans = 0; for(int i = 0; i < m - 1; i++){ for(int j = i + 1; j < m; j++){ // 두 우주가 균등한지 확인 (Combination) if(u[i].size() == u[j].size()){ // 좌표 압축 후, 좌표의 개수가 같을 때만 균등할 수 있음 bool equal = true; // 두 우주가 균등한지를 나타내는 Flag 변수 for(int k = 0; k < u[i].size(); k++){ // 각각의 좌표 압축 결과를 비교 if(u[i][k] != u[j][k]) equal = false; // 결과가 다르다면, 두 우주가 균등하지 않은 것 } if(equal) ans++; } } } cout << ans; }
'Algorithm > Binary Search' 카테고리의 다른 글
[백준 3151번] 합이 0 (C++) (0) | 2022.05.13 |
---|---|
[백준 2467번] 용액 (C++) (0) | 2022.05.13 |
[백준 16401번] 과자 나눠주기 (C++) (0) | 2022.05.12 |
[백준 2295번] 세 수의 합 (C++) (0) | 2022.05.12 |
[백준 17219번] 비밀번호 찾기 (C++) (0) | 2022.04.05 |