티스토리 뷰
문제 : https://programmers.co.kr/learn/courses/30/lessons/1833
주어진쐐기들 중 2가지를 고르는 것이 기본적인 풀이 방법이라 생각되어 진행하였습니다. O(N^2)
입력 값의 변형, 쐐기들끼리 선 긋기, 기울기를 이용한 풀이...여러가지를 생각하였지만 기존의 내 생각으론 풀 수 없었습니다.
먼저 해답을 보니 'Prefix sum'에 대해 설명하고 있었습니다.
그런데 어느 블로그에는 'segment tree'를 이용한 풀이라고 설명하고 있습니다.
둘다 처음엔 뭔가 했지만 하나씩 찾아보고 이해하였고, 자세한건 알고리즘 개념 메뉴에서 위의 두 개념에 대해 설명하고 있습니다.
다시 문제로 돌아가서 'Prefix sum'을 사용하여 구하고자 하는 구역에 쐐기가 있는지 판단합니다.
1. 주어진 범위의 직사각형 내부에 쐐기가 몇개 있는지를 먼저 구해놓는다 -> O(N^2)
S[i][j] : (0, 0) ~ (i, j) 범위의 직사각형 내부에 존재하는 쐐기의 개수
2. 그리고 모든 쐐기의 쌍에 대하여 -> O(N^2)
미리 구해놓은 S 배열의 값을 검사하여
값이 0인 경우 (내부에 쐐기가 없음) 텐트를 설치할 수 있음을 쉽게 판단할 수 있게됩니다.
그림 출처 : https://jaejin0me.github.io/post/algo46/
(x, y)와 (x2, y2)를 내부쐐기로 선정한다고 하였을때, 내부 쐐기 = S[x2-1][y2-1] - S[x2-1][y] - S[x][y2-1] + S[x][y] 이다.
구하고자 하는 것이 파란박스임을 생각한다면 도출될 수 있는 결론입니다.
위의 식을 실행하기 전에 우선 좌표압축을 실시합니다
좌표압축은 좌표들의 값들이 각각 특정한 의미를 지닌다기보다는, 위치의 순서를 나타낼때 사용하기 좋습니다.
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
|
int S[5000][5000]; //내부 쐐기의 개수 저장 배열
void location_compression(int n, vector<vector<int>>& data) {
vector<int> xpoints; // x좌표 집합
vector<int> ypoints; // y좌표 집합
for (int i = 0; i < data.size(); i++) { //데이터에 있는 좌표를 x,y로 나누어 넣음
xpoints.push_back(data[i][0]);
ypoints.push_back(data[i][1]);
}
unordered_set<int> uniqXpoints(xpoints.begin(), xpoints.end()); // 중복 값 제거를 위해 셋에 넣음
unordered_set<int> uniqYpoints(ypoints.begin(), ypoints.end());
xpoints.assign(uniqXpoints.begin(), uniqXpoints.end()); //중복제거된 값을 다시 넣기
ypoints.assign(uniqYpoints.begin(), uniqYpoints.end());
sort(xpoints.begin(), xpoints.end()); // 정렬
sort(ypoints.begin(), ypoints.end());
int x = 0, y = 0; //좌표 압축
for (int i = 0; i < data.size(); i++) {
for (int j = 0; j < xpoints.size(); j++) {
if (data[i][0] == xpoints[j]) {
x = j;
data[i][0] = x; // 좌표를 인덱스로 교체
break;
}
}
for (int k = 0; k < ypoints.size(); k++) {
if (data[i][1] == ypoints[k]) {
y = k;
data[i][1] = y; // 좌표를 인덱스로 교체
break;
}
}
S[x][y] = 1; // 쐐기가 있는 곳은 미리 1로 설정
}
return;
}
|
위의 과정으로 좌표 압축을 하고
1
2
3
4
5
6
7
8
9
10
11
12
|
// 각 영역별 쐐기 값을 누적으로 구하기
// 인덱스에 -1을 하므로, 범위에 벗어나지 않도록 주의한다.
// 차례대로 하는 이유가 있다!
void prefix_sum(int n) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
S[i][j] += (i-1 > -1 ? S[i-1][j]:0) +(j-1> -1 ? S[i][j-1]:0)
- (i-1> -1 && j-1> -1 ? S[i-1][j-1]:0);
}
}
|
각 영역별 쐐기 값을 누적으로 구하였습니다. S행렬의 순서 없이 위 과정을 진행할 경우
저장한 값들이 꼬이는 경우가 발생합니다.
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
|
void processing(int n, vector<vector<int>>& data) {
int ans = 0;
int cnt = 0;
int startX, startY, endX, endY;
// 모든 쐐기에 대해 조사
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 크기 1이상의 직사각형이 아닌경우
if (data[i][0] == data[j][0] || data[i][1] == data[j][1]) continue;
// 크기 1이상의 직사각형이 존재하는 경우
cnt = 0;
startX = data[i][0] < data[j][0] ? data[i][0] : data[j][0];
startY = data[i][1] < data[j][1] ? data[i][1] : data[j][1];
endX = data[i][0] > data[j][0] ? data[i][0] : data[j][0];
endY = data[i][1] > data[j][1] ? data[i][1] : data[j][1];
//내부의 공간이 1 인 경우, 쐐기가 있을 공간이 없음
if (startX + 1 > endX - 1 || startY + 1 > endY - 1) {
cnt = 0;
}
else {
//
cnt = S[endX - 1][endY - 1] - S[endX - 1][startY] - S[startX][endY - 1] + S[startX][startY];
}
if (cnt == 0) ans++; //내부에 존재하는 쐐기의 개수가 0이면 가능한 경우
}
}
return ans;
}
|
모든 쐐기에 대하여 텐트를 설치할 수 있는지 없는지 확인합니다.
확인하는 과정은 미리 계산해 놓은 prefix_sum 때문에 많은 시간이 걸리지 않습니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
int solution(int n, vector<vector<int>> data) {
int answer = 0;
vector<vector<int>> mydata = data;
memset(S, 0, sizeof(S)); // 0으로 세팅
location_compression(n, mydata);
prefix_sum(n);
answer = processing(n, mydata);
return answer;
}
|
메인함수라고 보시면 됩니다.