백준 17779 게리맨더링 2
문제
재현시의 시장 구재현은 지난 몇 년간 게리맨더링을 통해서 자신의 당에게 유리하게 선거구를 획정했다. 견제할 권력이 없어진 구재현은 권력을 매우 부당하게 행사했고, 심지어는 시의 이름도 재현시로 변경했다. 이번 선거에서는 최대한 공평하게 선거구를 획정하려고 한다.
재현시는 크기가 N×N인 격자로 나타낼 수 있다. 격자의 각 칸은 구역을 의미하고, r행 c열에 있는 구역은 (r, c)로 나타낼 수 있다. 구역을 다섯 개의 선거구로 나눠야 하고, 각 구역은 다섯 선거구 중 하나에 포함되어야 한다. 선거구는 구역을 적어도 하나 포함해야 하고, 한 선거구에 포함되어 있는 구역은 모두 연결되어 있어야 한다. 구역 A에서 인접한 구역을 통해서 구역 B로 갈 수 있을 때, 두 구역은 연결되어 있다고 한다. 중간에 통하는 인접한 구역은 0개 이상이어야 하고, 모두 같은 선거구에 포함된 구역이어야 한다.
선거구를 나누는 방법은 다음과 같다.
- 기준점 (x, y)와 경계의 길이 d1, d2를 정한다. (d1, d2 ≥ 1, 1 ≤ x < x+d1+d2 ≤ N, 1 ≤ y-d1 < y < y+d2 ≤ N)
- 다음 칸은 경계선이다.
- (x, y), (x+1, y-1), ..., (x+d1, y-d1)
- (x, y), (x+1, y+1), ..., (x+d2, y+d2)
- (x+d1, y-d1), (x+d1+1, y-d1+1), ... (x+d1+d2, y-d1+d2)
- (x+d2, y+d2), (x+d2+1, y+d2-1), ..., (x+d2+d1, y+d2-d1)
- 경계선과 경계선의 안에 포함되어있는 5번 선거구이다.
- 5번 선거구에 포함되지 않은 구역 (r, c)의 선거구 번호는 다음 기준을 따른다.
- 1번 선거구: 1 ≤ r < x+d1, 1 ≤ c ≤ y
- 2번 선거구: 1 ≤ r ≤ x+d2, y < c ≤ N
- 3번 선거구: x+d1 ≤ r ≤ N, 1 ≤ c < y-d1+d2
- 4번 선거구: x+d2 < r ≤ N, y-d1+d2 ≤ c ≤ N
아래는 크기가 7×7인 재현시를 다섯 개의 선거구로 나눈 방법의 예시이다.
x = 4, y = 3, d1 = 1, d2 = 1 | x = 2, y = 5, d1 = 3, d2 = 2 | x = 2, y = 4, d1 = 2, d2 = 2 |
구역 (r, c)의 인구는 A[r][c]이고, 선거구의 인구는 선거구에 포함된 구역의 인구를 모두 합한 값이다. 선거구를 나누는 방법 중에서, 인구가 가장 많은 선거구와 가장 적은 선거구의 인구 차이의 최솟값을 구해보자.
더보기
예제 입력 1
6
1 2 3 4 1 6
7 8 9 1 4 2
2 3 4 1 1 3
6 6 6 6 9 4
9 1 9 1 9 5
1 1 1 1 9 9
예제 출력 1
18
#include<iostream>
#include<algorithm>
using namespace std;
int N;
// x값이 위아래, y값이 왼오, d1은 왼위오아, d2는 오위왼아
bool check(int x, int y, int d1, int d2) {
// x == y == 0 or x == y == N - 1 이면 나눌 수 없음
if ((x == 0 || x == N - 1) && (x == y)) return false;
// CASE 1
if (x + d1 >= N || y - d1 < 0) return false;
// CASE 2
if (x + d2 >= N || y + d2 >= N) return false;
// CASE 3
if (x + d1 + d2 >= N || y - d1 + d2 >= N || y - d1 + d2 < 0) return false;
// CASE 4
if (x + d1 + d2 >= N || y + d1 - d2 >= N || y + d1 - d2 < 0) return false;
return true;
}
int leftUp(int** arr, int x, int y, int d1, int d2) {
int sum = 0;
int tmp = y;
for (int i = 0; i < x + d1; i++) {
tmp = (i >= x ? tmp - 1 : tmp);
for (int j = 0; j <= tmp; j++) {
sum += arr[i][j];
}
}
return sum;
}
int rightUp(int** arr, int x, int y, int d1, int d2) {
int sum = 0;
int tmp = y + 1;
for (int i = 0; i <= x + d2; i++) {
tmp = (i > x ? tmp + 1 : tmp);
for (int j = N - 1; j >= tmp; j--) {
sum += arr[i][j];
}
}
return sum;
}
int midCenter(int** arr, int x, int y, int d1, int d2) {
int sum = 0;
int cnt = 1;
int tmp = 1;
int y_pos = y;
for (int i = x; i <= x + d1 + d2; i++) {
for (int j = y_pos; j < y_pos + tmp; j++) {
sum += arr[i][j];
}
if (cnt <= d1) {
tmp++;
y_pos--;
}
else {
y_pos++;
tmp--;
}
if (cnt <= d2) tmp++;
else tmp--;
cnt++;
}
return sum;
}
int leftDown(int** arr, int x, int y, int d1, int d2) {
int sum = 0;
int tmp = y - d1 - 1;
for (int i = x + d1; i < N; i++) {
for (int j = 0; j <= tmp; j++) {
sum += arr[i][j];
}
tmp = (i >= x + d1 + d2 ? tmp : tmp + 1);
}
return sum;
}
int rightDown(int** arr, int x, int y, int d1, int d2) {
int sum = 0;
int tmp = y + d2;
for (int i = x + d2 + 1; i < N; i++) {
for (int j = N - 1; j >= tmp; j--) {
sum += arr[i][j];
}
tmp = (i <= x + d1 + d2 ? tmp - 1 : tmp);
}
return sum;
}
int main() {
/* INIT Phase */
cin >> N;
int** board = new int* [N];
for (int i = 0; i < N; i++)
board[i] = new int[N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
cin >> board[i][j];
}
/* SOLVE Phase */
// X값은 위아래, Y값은 왼오
int result = INT32_MAX;
for (int PIVOT_X = 0; PIVOT_X < N; PIVOT_X++) {
for (int PIVOT_Y = 0; PIVOT_Y < N; PIVOT_Y++) {
// d1값은 X(왼위,오아), d2값은 Y(오위,왼아)
for (int d1 = 1; d1 < N; d1++) {
for (int d2 = 1; d2 < N; d2++) {
//구획이 5개로 나뉘는지 확인하고, 안되면 건너 뛴다.
if (!check(PIVOT_X, PIVOT_Y, d1, d2)) continue;
int lu = leftUp(board, PIVOT_X, PIVOT_Y, d1, d2);
int ru = rightUp(board, PIVOT_X, PIVOT_Y, d1, d2);
int ce = midCenter(board, PIVOT_X, PIVOT_Y, d1, d2);
int ld = leftDown(board, PIVOT_X, PIVOT_Y, d1, d2);
int rd = rightDown(board, PIVOT_X, PIVOT_Y, d1, d2);
int maxValue = max(lu, max(ru, max(ce, max(ld, rd))));
int minValue = min(lu, min(ru, min(ce, min(ld, rd))));
result = min(result, maxValue - minValue);
}
}
}
}
/* PRINT Phase */
cout << result;
/* FINAL Phase */
for (int i = 0; i < N; i++)
delete[] board[i];
delete[] board;
return 0;
}
'알고리즘' 카테고리의 다른 글
[백준] 2468 - 안전 영역 (0) | 2019.11.30 |
---|---|
[백준] 7569 - 토마토 (0) | 2019.11.16 |
[백준] 17837 - 새로운 게임 2 (0) | 2019.11.02 |
[백준] 17822 - 원판돌리기 (0) | 2019.11.01 |
[백준] 17612 - 쇼핑몰 (0) | 2019.10.25 |