백준 18870번 "좌표 압축"
Question:
수직선 위에 N개의 좌표 X1, X2, …, XN이 있다.
이 좌표에 좌표 압축을 적용하려고 한다.
Xi를 좌표 압축한 결과
X’i의 값은
Xi > Xj를 만족하는
서로 다른 좌표의 개수와 같아야 한다.
X1, X2, …, XN에 좌표 압축을 적용한 결과
X’1, X’2, …, X’N를 출력해보자.
Input:
첫째 줄에 N이 주어진다.
둘째 줄에는 공백 한 칸으로 구분된 X1, X2, …, XN이 주어진다.
Output:
첫째 줄에 X’1, X’2, …, X’N을 공백 한 칸으로 구분해서 출력한다.
Restriction:
시간제한:
2 초
조건:
- 1 ≤ N ≤ 1,000,000
- -109 ≤ Xi ≤ 109
Example Case:
Input case 1:
5
2 4 -10 4 -9
Output case 1:
2 3 0 3 1
Input case 2:
6
1000 999 1000 999 1000 999
Output case 2:
1 0 1 0 1 0
풀이 과정:
좌표 압축 기법은,
다루는 데이터의 범위가 넓지만
다루는 데이터의 밀도가 낮을 때(수가 작을때),
메모리 낭비를 막기 위하여 데이터를 변환한다.
각 데이터의 좌표마다 인덱싱을 통해 정렬된 순서로 번호를 매긴다.
변환한 데이터를 이분탐색을 통하여 비교적 짧은시간(log n)안에 탐색 할 수 있다.
이 문제에 한해서 필자는 tuple을 통해 풀었지만,
(tuple <int,int,int> = (본래 데이터 / 본래 데이터 정렬 순서 / 변환 좌표 순서))
정렬 후 중복되는 값을 지우고 난 뒤
c++의 lower_bound(이분 탐색)을 통해 해당 값에 대한 인덱스를 편하게 받아 올 수 있다.
아래 코드는 lower_bound의 예시이다.
당연하게도, 이분탐색을 하려면 값들이 오름차순으로 정렬되어 있어야한다.
(*upper_bound는 찾으려는 key값을 초과하는 숫자의 첫 index 반환)
1
2
3
4
5
6
7
int arr[6] = { 1,2,3,4,5,6 };
int target = 3;
cout << lower_bound(arr,arr+6,target) - arr;
//arr 처음부터 끝까지 target값 보다 큰 숫자가 처음으로 나오는 위치의 인덱스 번호를 반환.
// lower_bound return형은 Iterator이기에, 실제로 몇 번째 인덱스인지 알고 싶다면
// lower_bound값에서 배열 첫 번째 주소를 가르키는 배열의 이름을 빼줘야한다.
// vector면 v.begin()을 빼주자.
아래의 코드는 해당 문제의 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>
using namespace std;
bool cmp(tuple <int,int,int> a,tuple <int,int,int> b) {
return get<1>(a) < get<1>(b);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n,temp;
cin >> n;
tuple <int,int,int> t1; // original value / original order / xi > xj cnt
vector <tuple <int,int,int>> v;
for(int i = 0;i<n;i++) {
cin >> temp;
t1 = make_tuple(temp,i,0);
v.push_back(t1);
}
sort(v.begin(),v.end()); //sort ascending
for(int i = 1;i<n;i++) { //compare only front val => sorted by first val
if(get<0>(v[i-1]) < get<0>(v[i])) {
get<2>(v[i]) = get<2>(v[i-1]) + 1;
}
else {
get<2>(v[i]) = get<2>(v[i-1]);
}
}
sort(v.begin(),v.end(),cmp); // sort back with original order
for(int i = 0;i<n;i++) {
cout << get<2>(v[i]) << " ";
}
}