Algorithm/구현(Brute force, Back tracking, etc.)
백준 17779 게리맨더링2
쩡류
2021. 11. 30. 19:51
728x90
https://www.acmicpc.net/problem/17779
17779번: 게리맨더링 2
재현시의 시장 구재현은 지난 몇 년간 게리맨더링을 통해서 자신의 당에게 유리하게 선거구를 획정했다. 견제할 권력이 없어진 구재현은 권력을 매우 부당하게 행사했고, 심지어는 시의 이름
www.acmicpc.net
- 브루트포스 + 빡구현 문제이다.
- 시간복잡도는, 최악의 경우 거의 O(N^6)까지도 도달할 수 있다. 하지만 N의 최대값이 20이므로 1억 이하이기 때문에 해당 알고리즘으로 통과가 가능하다.
- 시작점 x,y와 모든 d1, d2에 대한 경우의 수들을 선택해서 계산을 해야 한다. 4중 for문으로 4가지 변수값을 선택했다면, area 배열에 구역을 입력 해 주면 된다. 일단
1. 5번 경계점들을 넣어주고,
2. 경계선 내부를 5로 채워주고
3. 차례대로 나머지 구역들을 채워준다.
여기서, 경계선 내부를 채워주는 연산을 잘 해야 하는데 왼쪽 위, 오른쪽 위는 y값을 기준으로, 왼쪽 아래, 오른쪽 아래는 y-d1+d2값을 기준으로 해서 5를 잘 채워주면 된다.
- 5번 구역만 잘 채워주면 쉽게 해결할 수 있는 문제이다.
#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
int man[21][21];
int area[21][21];
int n;
int getpartOne(int x, int y, int d1, int d2) {
int sum = 0;
for (int i = 1; i < x + d1; i++) {
for (int j = 1; j <= y; j++) {
if (area[i][j] == 5) continue;
sum += man[i][j];
}
}
return sum;
}
int getpartTwo(int x, int y, int d1, int d2) {
int sum = 0;
for (int i = 1; i <= x + d2; i++) {
for (int j = y + 1; j <= n; j++) {
if (area[i][j] == 5) continue;
sum += man[i][j];
}
}
return sum;
}
int getpartThree(int x, int y, int d1, int d2) {
int sum = 0;
for (int i = x + d1; i <= n; i++) {
for (int j = 1; j < y - d1 + d2; j++) {
if (area[i][j] == 5) continue;
sum += man[i][j];
}
}
return sum;
}
int getpartFour(int x, int y, int d1, int d2) {
int sum = 0;
for (int i = x + d2 + 1; i <= n; i++) {
for (int j = y - d1 + d2; j <= n; j++) {
if (area[i][j] == 5) continue;
sum += man[i][j];
}
}
return sum;
}
void divide(int x, int y, int d1, int d2) {
memset(area, 0, sizeof(area));
area[x][y] = 5;
//1
int nx = x;
int ny = y;
for (int i = 0; i < d1; i++) {
nx += 1; ny -= 1;
if (nx < 1 || nx > n || ny < 1 || ny > n) continue;
for (int j = ny; j <= y; j++) {
area[nx][j] = 5;
}
}
//2
nx = x;
ny = y;
for (int k = 0; k < d2; k++) {
nx += 1; ny += 1;
if (nx < 1 || nx > n || ny < 1 || ny > n) continue;
for (int j = y; j <= ny; j++) {
area[nx][j] = 5;
}
}
//3
nx = x + d1;
ny = y - d1;
area[nx][ny] = 5;
for (int k = 0; k < d2; k++) {
nx += 1; ny += 1;
area[nx][ny] = 5;
for(int j = ny; j <= y - d1 + d2; j++){
area[nx][j] = 5;
}
}
nx = x + d2;
ny = y + d2;
area[nx][ny] = 5;
for (int k = 0; k < d1; k++) {
nx += 1; ny -= 1;
if (nx < 1 || nx > n || ny < 1 || ny > n) continue;
for (int j = y-d1+d2; j <= ny; j++) {
area[nx][j] = 5;
}
}
}
int main() {
cin >> n;
int total_arr = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> man[i][j];
total_arr += man[i][j];
}
}
//각 칸을 돌면서 기준점 정함
int answer = 2100000000;
for (int x = 1; x <= n; x++) { //행
for (int y = 1; y <= n; y++) { //열
for (int d1 = 1; d1 <= 20; d1++) {
for (int d2 = 1; d2 <= 20; d2++) {
if (x + d1 + d2 > n || y-d1<1 || y+d2 >n) break;
divide(x, y, d1, d2);
int sum1 = getpartOne(x, y, d1, d2);
int sum2 = getpartTwo(x, y, d1, d2);
int sum3 = getpartThree(x, y, d1, d2);
int sum4 = getpartFour(x, y, d1, d2);
int sum5 = total_arr - (sum1 + sum2 + sum3 + sum4);
//각 선거구역 크기는 최소 1칸 이상
if (sum1 >= 1 && sum2 >= 1 && sum3 >= 1 && sum4 >= 1 && sum5 >= 1) {
vector<int> sum_of_area = { sum1, sum2, sum3, sum4, sum5 };
sort(sum_of_area.begin(), sum_of_area.end());
answer = min(answer, sum_of_area[4] - sum_of_area[0]);
}
}
}
}
}
cout << answer;
return 0;
}