티스토리 뷰
1. 문제 링크
https://www.acmicpc.net/problem/17779
17779번: 게리맨더링 2
재현시의 시장 구재현은 지난 몇 년간 게리맨더링을 통해서 자신의 당에게 유리하게 선거구를 획정했다. 견제할 권력이 없어진 구재현은 권력을 매우 부당하게 행사했고, 심지어는 시의 이름도 재현시로 변경했다. 이번 선거에서는 최대한 공평하게 선거구를 획정하려고 한다. 재현시는 크기가 N×N인 격자로 나타낼 수 있다. 격자의 각 칸은 구역을 의미하고, r행 c열에 있는 구역은 (r, c)로 나타낼 수 있다. 구역을 다섯 개의 선거구로 나눠야 하고, 각 구역은 다
www.acmicpc.net
2. 문제 개요
선거구를 적절히 나누어 인구가 가장 많은 선거구와 가장 적은 선거구의 인구 차이의 최솟값을 구해보자.
3. 문제 힌트
구현력 -> 경계선, 각 선거구의 구역 범위 처리 잘하기.
Brute Force . N이 최대 20밖에 안됨. 모든 칸을 선거구 5의 기준점이라 가정, 모든 범위(d1, d2의 모든 경우의 수)를 탐색해도 무리가 없음.
4. 문제 풀이
이 문제는 문제에 주어진 조건을 얼마나 코드로 잘 옮기느냐에 따라서 난이도가 결정된다고 생각한다.
경계선의 범위나 각 선거구의 범위는 문제에 잘 주어져 있다.
우선 보기나 예에서 x,y가 선거구 5의 가장 윗점이므로, x, y 선거구 5의 윗점이라 가정하고 모두 (N*N) 탐색한다.
기준점을 정하고 d1, d2를 (1~최대 N-1)까지 늘려보고 구역을 분리한다.
구역을 정할 때는 BFS를 사용하여 정한다.
그렇게 구역을 분리하고 선거구 별 인구를 합해서 인구 차이가 최소가 되는 값을 찾는다.
-반복-
5. 코드
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int n;
int a[20][20];
int ret = 123456789;
const int dx[] = { 1,-1,0,0 };
const int dy[] = { 0,0,1,-1 };
int main()
{
cin >> n;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
cin >> a[i][j];
for (int x = 0; x < n-2; ++x)
{
for (int y = 1; y < n - 1; ++y)
{
for (int d1 = 1; d1 < y; ++d1)
{
for (int d2 = 1; y + d2 < n; ++d2)
{
//boundary check;
if (x + d1 + d2 >= n) continue;
if (y - d1 < 0 || y + d2 >= n) continue;
//draw a line which have 5 value
int mask[20][20] = { 0 };
for (int i = 0; i <= d1; ++i)
{
mask[x + i][y - i] = 5;
mask[x + d2 + i][y + d2 - i] = 5;
}
for (int i = 0; i <= d2; ++i)
{
mask[x + i][y + i] = 5;
mask[x + d1 + i][y - d1 + i] = 5;
}
//BFS
queue <pair<int, int>> q;
bool visited[20][20] = { false };
q.push(make_pair(0, 0)); //1
mask[0][0] = 1;
q.push(make_pair(0, n - 1)); //2
mask[0][n - 1] = 2;
q.push(make_pair(n - 1, 0)); //3
mask[n - 1][0] = 3;
q.push(make_pair(n - 1, n - 1));//4
mask[n - 1][n - 1] = 4;
visited[0][0] = true;
visited[0][n - 1] = true;
visited[n - 1][0] = true;
visited[n - 1][n - 1] = true;
while (!q.empty())
{
int cx = q.front().first;
int cy = q.front().second;
q.pop();
for (int dir = 0; dir < 4; ++dir)
{
int nx = cx + dx[dir];
int ny = cy + dy[dir];
if (nx < 0 || ny < 0 || nx >= n || ny >= n || visited[nx][ny] || mask[nx][ny] == 5)
continue;
//masking
if ((0 <= nx && nx < x + d1) && (0 <= ny && ny <= y))
mask[nx][ny] = 1;
if ((0 <= nx && nx <= x + d2) && (y < ny && ny < n))
mask[nx][ny] = 2;
if ((x + d1 <= nx && nx < n) && (0 <= ny && ny < y - d1 + d2))
mask[nx][ny] = 3;
if ((x + d2 < nx && nx < n) && (y - d1 + d2 <= ny && ny < n))
mask[nx][ny] = 4;
visited[nx][ny] = true;
q.push(make_pair(nx, ny));
}
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (mask[i][j] == 0)
mask[i][j] = 5;
//separation finished.
int minval = 123456789;
int maxval = -123456789;
int val[5] = { 0 };
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (mask[i][j] == 1)
{
val[0] += a[i][j];
}
else if (mask[i][j] == 2)
{
val[1] += a[i][j];
}
else if (mask[i][j] == 3)
{
val[2] += a[i][j];
}
else if (mask[i][j] == 4)
{
val[3] += a[i][j];
}
else if (mask[i][j] == 5)
{
val[4] += a[i][j];
}
}
}
for (int i = 0; i < 5; ++i)
{
minval = min(minval, val[i]);
maxval = max(maxval, val[i]);
}
ret = min(ret, maxval - minval);
}
}
}
}
cout << ret;
return 0;
}
6. 결과 사진
※이 알고리즘으로는 약 80ms가 최대인 것 같다. 채점 현황을 보니 C++14 20ms가 나오던데 다른 분들의 풀이도 참고해봐야겠다.
지적, 댓글 언제나 환영입니다~
'알고리즘 > 삼성 SW테스트' 카테고리의 다른 글
boj,백준 ) 17142. 연구소 3 (C / C++) (0) | 2020.03.05 |
---|---|
boj, 백준) 17822. 원판 돌리기 (C / C++) (0) | 2020.03.03 |
boj, 백준) 17472. 다리 만들기2 ( C / C++) (0) | 2020.02.29 |
boj, 백준) 17471. 게리맨더링(C / C++) (0) | 2020.02.29 |
boj, 백준) 17406. 배열 돌리기 4 ( C / C++) (0) | 2020.02.29 |