알고리즘 테크닉 - 좌표압축(grid compression)
1. 좌표압축이란
정점 번호가 1에서 $10^{9}$사이의 값으로 이루어져 있는 그래프가 하나 주어질때,
1번 정점에서 시작하여, 방문 가능한 서로 다른 노드의 수를 구하는 프로그램을 작성하세요.
일반적인 DFS 탐색으로는 1번 정점에서 N번 정점까지 번호가 매겨져 있어서,
큰 문제 없이 크기가 N인 visited 배열과 간선 정보를 나타내는 인접 리스트를 만들어 해결할 수 있습니다.
하지만 위와 같이 정점 번호가 1번에서 $10^{9}$사이로 주어진다면, visited 배열을 만드는 순간 메모리 초과로 일반적인 방법으로는 해결하기 어렵다.
그런데 조금만 생각해보면... 주어진 정점 번호를 오름차순으로 나열할때, 1,4,6,7,30,2000,$10^{9}$밖에 없으며..
이 정점들의 번호를 1번부터 순서대로 다시 매겨준다면...
1 -> 1
4 -> 2
6 -> 3
7 -> 4
30 -> 5
2000 -> 6
10^9 -> 7
이렇게 정점 번호를 변경한다면.. 마치 기존에 1~n번 사이의 번호로 주어진 문제처럼 해결할 수 있게 된다.
이를 좌표압축(grid compression)이라고 부른다.
위 과정은 다음 두 과정을 거쳐 진행할 수 있다.
1) 입력으로 들어온 모든 값을 treeset에 넣어준다.
2) 작은 숫자부터 순서대로 뽑아, 각 숫자에 번호를 매기고 그 결과를 hashmap에 넣어준다.
위의 예에서는 (1,1), (4,2), (6,3), (7,4), (30,5), (2000,5), (10^9,7) 형태로 hashmap에 들어가게 된다.
2. 구현 예시
파이썬부터 생각해보면.. 정점 번호를 오름차순으로 정렬하고 번호를 다시 매겨주면 되는데
edges = [(1,10**9), (1,2000), (1,4), (30,10**9), (6,7)]
nums = set()
#사용되는 모든 정점의 번호를 set에 넣어준다.
for v1,v2 in edges:
nums.add(v1)
nums.add(v2)
#sorted()를 이용해 오름차순 정렬된 리스트를 생성
sorted_nums = sorted(nums)
#grid compression
compression = dict()
i = 1
for num in sorted_nums:
compression[num] = i
print(num, "->", i)
i += 1
#간선을, 압축한 정점 번호로 변경
for i in range(5):
v1,v2 = edges[i]
edges[i] = (compression[v1], compression[v2])
1 -> 1
4 -> 2
6 -> 3
7 -> 4
30 -> 5
2000 -> 6
1000000000 -> 7
자바로 구현하면 다음과 같다.
treeset이 뭔지 공부해야겠는데??
정렬된 set인것 같기는한데, 뭔가 정렬이 안되어있는데 정렬이 되어있는?
균형잡힌 이진탐색트리라고 하는데
import java.util.TreeSet;
import java.util.HashMap;
class Pair {
int x,y;
public Pair(int x, int y){
this.x = x;
this.y = y;
}
};
public class Main {
public static void main(String[] args) {
Pair[] edges = new Pair[]{
new Pair(1,(int)1e9),
new Pair(1,2000),
new Pair(1,4),
new Pair(30,(int)1e9),
new Pair(6,7),
};
TreeSet<Integer> nums = new TreeSet<>();
//사용되는 모든 번호를 treeset에 넣어준다.
for(int i = 0; i< 5; i++){
nums.add(edges[i].x);
nums.add(edges[i].y);
}
//treeset에서 정점을 작은 번호부터 뽑아
//각 정점별로 1번부터 순서대로 매칭하여
// 그 결과를 hashmap에 넣어준다.
HashMap<Integer, Integer> mapper = new HashMap<>();
int cnt = 1;
//treeset을 순회하는 법으로 Integer num: nums로 간단하게
for(Integer num: nums){
mapper.put(num,cnt);
//기존 정점번호마다 어떤 번호로
//정해졌는지 출력
System.out.println(num + " -> " + cnt);
cnt++;
}
//주어진 간선을 이루는 정점 번호를
//새로운 정점 번호로 변경
for(int i = 0; i < 5; i++){
edges[i] = new Pair(
mapper.get(edges[i].x),
mapper.get(edges[i].y)
);
}
}
1 -> 1
4 -> 2
6 -> 3
7 -> 4
30 -> 5
2000 -> 6
1000000000 -> 7
3. 연습문제
18870번: 좌표 압축
수직선 위에 N개의 좌표 X1, X2, ..., XN이 있다. 이 좌표에 좌표 압축을 적용하려고 한다. Xi를 좌표 압축한 결과 X'i의 값은 Xi > Xj를 만족하는 서로 다른 좌표의 개수와 같아야 한다. X1, X2, ..., XN에 좌
www.acmicpc.net
4. 풀이
압축하는 방법을 문제에서 제시해줬으니까, 그대로 구현하면 될것 같다
근데 풀어보니까 결국 위에서 구현한 좌표압축이랑 똑같은거임
from sys import stdin
n = int(stdin.readline())
grid = list(map(int,stdin.readline().split()))
sorted_grid = sorted(set(grid))
compression = {}
i = 0
for g in sorted_grid:
compression[g] = i
i += 1
for g in grid:
print(compression[g], end = " ")
'알고리즘 > 압축 알고리즘' 카테고리의 다른 글
값 압축(value compression) 기본 문제로 연습 (0) | 2023.04.28 |
---|