IT recording...

[BOJ] 17779 게리맨더링 2 -Java 본문

Algorithm

[BOJ] 17779 게리맨더링 2 -Java

I-one 2022. 4. 25. 21:07

https://www.acmicpc.net/problem/17779

 

17779번: 게리맨더링 2

재현시의 시장 구재현은 지난 몇 년간 게리맨더링을 통해서 자신의 당에게 유리하게 선거구를 획정했다. 견제할 권력이 없어진 구재현은 권력을 매우 부당하게 행사했고, 심지어는 시의 이름

www.acmicpc.net

[풀이]

시간 제한 1초에 총 1억번 연산이다.

여기서 N은 20까지, 우리가 정해야 할 것은 1<=r,c,d1,d2<=20이다.

=> O(N^4) =  160,000 => 삽가능이다.

(왜 4중 for문은 안된다고만 생각했을까.. 왜 다른 범위 조건을 내가 찾아야 한다고 생각했을까.. 시간복잡도 볼껄..)

 

0. 문제에서 x는 row이고, y는 col이다. (헷갈려서 몇 번 엎었다.)

 

1. 우선 좌표 중에서 어느 점을 기준점으로 할 것인지, d1과 d2를 어떻게 설정할 것인지를 정해야 한다.

4중for문으로 구하고, 범위에 해당하는지로 가능한지를 살핀다.

 (d1, d2 ≥ 1, 1 ≤ x < x+d1+d2 ≤ N, 1 ≤ y-d1 < y < y+d2 ≤ N)

//r,c쌍
for(int r=1;r<=N;r++) {
   for(int c=1;c<=N;c++) {
      //d1,d2찾기
      for(int d1=1;d1<=N;d1++) {
         for(int d2=1;d2<=N;d2++) {
            if(r+d1+d2<=N && 1<=c-d1 && c<c+d2 && c+d2<=N){
               //여기서 각 지역의 인구수 구하기
               int small = getSmall(r,c,d1,d2);
               answer = Math.min(answer,small);
            }
         }
      }
   }
}

2. r,c,d1,d2가 정해졌으면 해당 부분에서 각 지역의 인구수들을 확인해야 한다.

경계선은 다음과 같다. 

  1. (x, y), (x+1, y-1), ..., (x+d1, y-d1)
  2. (x, y), (x+1, y+1), ..., (x+d2, y+d2)
  3. (x+d1, y-d1), (x+d1+1, y-d1+1), ... (x+d1+d2, y-d1+d2)
  4. (x+d2, y+d2), (x+d2+1, y+d2-1), ..., (x+d2+d1, y+d2-d1)

선거구의 범위는 다음과 같다.

  • 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

1번 선거구는 위의 범위에서, 5번에 해당하지 않는 부분이다. 2,3,4번도 마찬가지.

근데 5번 선거구 안쪽의 범위를 확정하는 것은 매우 어렵다. (이걸 정해보려고 하다가 N시간을 날렸다... 흑..)

그러므로 경계선을 그려놓고, 이 경계선 전까지를 각 구의 범위로 확정하자. (1,3번은 왼쪽에서부터, 2,4번은 오른쪽에서부터 시작한다.)

 

3. 각 선거구의 인원을 HashMap에 넣어두고, 모든 좌표를 다 돌았으면 ArrayList에 넣고 인원수로 정렬한다.

5번선거구는 구하기 어려우므로 총 인원수(total)에서 1,2,3,4의 인원수를 빼서 구한다.

 

4. 절댓값 중 가장 작은 것을 구하면 끝

 

[코드]

import java.util.*;
import java.io.*;

public class Main {
   static StringTokenizer st;
   static int N;
   static int[][] Map;
   static int answer = Integer.MAX_VALUE;
   static int total = 0;
   
   public static void main(String[] args) throws Exception{
      // TODO Auto-generated method stub
      //System.setIn(new FileInputStream("src/main/java/input.txt"));
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      N = Integer.parseInt(br.readLine());
      Map = new int[N+1][N+1];
      
      for(int i=1;i<N+1;i++) {
         st = new StringTokenizer(br.readLine());
         for(int j=1;j<N+1;j++) {
            Map[i][j] = Integer.parseInt(st.nextToken());
            total += Map[i][j];
         }
      }
      
      //r,c쌍
      for(int r=1;r<=N;r++) {
         for(int c=1;c<=N;c++) {
            //d1,d2찾기
            for(int d1=1;d1<=N;d1++) {
               for(int d2=1;d2<=N;d2++) {
                  if(r+d1+d2<=N && 1<=c-d1 && c<c+d2 && c+d2<=N){
                     //여기서 각 지역의 인구수 구하기
                     int small = getSmall(r,c,d1,d2);
                     answer = Math.min(answer,small);
                  }
               }
            }
         }
      }
      
      System.out.println(answer);
   }
   
   static int getSmall(int row, int col, int d1, int d2) {
      HashMap<Integer,Integer> hashMap = new HashMap<>();

      //경계 설정
      boolean[][] visit = new boolean[N+1][N+1];
      for(int i=0;i<=d1;i++){
         visit[row+i][col-i] = true;
         visit[row+d2+i][col+d2-i] = true;
      }
      for(int i=0;i<=d2;i++){
         visit[row+i][col+i] = true;
         visit[row+d1+i][col-d1+i] = true;
      }


      for(int i=1;i<row+d1;i++){
         for(int j=1;j<=col;j++){
            if(visit[i][j]){
               break;
            }
            if(hashMap.containsKey(1)) {
               hashMap.put(1, hashMap.get(1)+Map[i][j]);
            }
            else {
               hashMap.put(1, Map[i][j]);
            }
         }
      }
      for(int i=1;i<=row+d2;i++){
         for(int j=N;j>col;j--){
            if(visit[i][j]){
               break;
            }
            if(hashMap.containsKey(2)) {
               hashMap.put(2, hashMap.get(2)+Map[i][j]);
            }
            else {
               hashMap.put(2, Map[i][j]);
            }

         }
      }
      for(int i=row+d1;i<=N;i++){
         for(int j=1;j<col-d1+d2;j++){
            if(visit[i][j]){
               break;
            }
            if(hashMap.containsKey(3)) {
               hashMap.put(3, hashMap.get(3)+Map[i][j]);
            }
            else {
               hashMap.put(3, Map[i][j]);
            }

         }
      }
      for(int i=row+d2+1;i<=N;i++){
         for(int j=N;j>=col-d1+d2;j--){
            if(visit[i][j]){
               break;
            }
            if(hashMap.containsKey(4)) {
               hashMap.put(4, hashMap.get(4)+Map[i][j]);
            }
            else {
               hashMap.put(4, Map[i][j]);
            }
         }
      }

      ArrayList<Point> Arr = new ArrayList<>();
      int oneToFour = 0; //5구역 = total - 1~4합(oneToFour)
      for(int key:hashMap.keySet()) {
         Arr.add(new Point(key,hashMap.get(key)));
         oneToFour += hashMap.get(key);
      }
      Arr.add(new Point(5,total - oneToFour));
      Collections.sort(Arr); //합 순으로 정렬

      return Math.abs(Arr.get(Arr.size()-1).num - Arr.get(0).num);
   }

   static class Point implements Comparable<Point>{
      int district;
      int num;

      public Point(int district, int num) {
         super();
         this.district = district;
         this.num = num;
      }
      @Override
      public int compareTo(Point o) {
         return Integer.compare(num, o.num);
      }
   }
}

[느낀점]

소요시간 : 6h...

나는 왜이리 어려웠을까, 왜 다른 범위를 내가 정해줘야 한다. 규칙을 찾아야 한다 라고 생각했을까

범위를 다 주어줬는데 ㅜㅜ 

그리고 앞에서부터 생각하는거 말고도, 뒤에서부터 생각하는 것도 머리에 넣어 두자

추가로 시간복잡도 꼭 고려해보자!!

 

'Algorithm' 카테고리의 다른 글

[BOJ] 17822 원판돌리기 - Java  (0) 2022.04.26
[BOJ] 17837 새로운게임2 - Java  (0) 2022.04.26
[BOJ] 16236 아기상어 - Java  (0) 2022.04.24
[BOJ] 17142 연구소3 - Java  (0) 2022.04.23
[BOJ] 17140 이차원배열과연산 - Java  (0) 2022.04.23
Comments