[백준] 17471 게리 맨더링 - JAVA

2024. 8. 10. 14:45·Algorithm

백준 17471 게리 맨더링 GoldIII - JAVA 📌

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

문제 설명

문제 풀이


접근 💡

- 1 부터 N 까지 숫자를 두 구역으로 나눌 수 있는 모든 경우의 수를 찾는다! < 백트래킹
- 구역을 나누고 한 구역 마다 인접해 있는지 확인한다! < BFS
- 인접해 있다면 두 구역의 인구수 차를 구해 최솟값으로 갱신해준다!

 

첫 번째, 숫자(지역)를 두 구역으로 나눠보자.

for (int i = 1; i <= N / 2; i++) {
	boolean[] visited = new boolean[N + 1];
    combine(1, i, new ArrayList<>(), visited);
}

//두 구역으로 나누는 메서드
public static void combine(int start, int end, ArrayList<Integer> groupA, boolean[] visited) {
	if (groupA.size() == end) {
    	ArrayList<Integer> groupB = new ArrayList<>();
        for (int i = 1; i <= N; i++) {
        	if (!visited[i]) {
            	groupB.add(i);
            }
        }
        return;
    }
    
    for (int i = start; i <= N; i++) {
    	visited[i] = true;
        groupA.add(i);
        combine(i + 1, end, groupA, visited)
        groupB.remove(groupA.size() - 1);
        visited[i] = false;
    }
}

왜 메인 메서드에서 for문의 범위가 N이 아닌 N / 2 일지 생각을 해봐야 한다.

 

N까지 반복을 한다 가정해보자.

- 구역이 나뉠 때 한 구역의 범위가 아래와 같이 될 것이다.

[1], [2,3,4,5,6]

[2], [1,3,4,5,6]

...

[6], [1,2,3,4,5]

...

[1,2,3], [4,5,6]

[1,2,4], [3,5,6]

...

[1,2,3,4,5], [6]

...

바로 위 구역이 나뉘어진 경우를 봐보자. 어랏? 중복이 발생하네!?

이렇게 구역이 나뉜 경우를 이미 탐색을 했을 것이다.

 

따라서.. 메인 메서드에서 for문의 범위를 N까지 해주어야 한다.

그럼 탐색의 마지막은

[4,5,6], [1,2,3] 이 될 것이고 이 경우, 모든 경우를 탐색한 것과 마찬가지가 된다.

 

두 번째, 나뉜 두 구역이 한 구역마다 인접해 있는지 확인 해본다.

public static boolean isLinked(ArrayList<Integer> group) {

    boolean[] visited = new boolean[N];
    Queue<Integer> q = new LinkedList<>();
    q.add(group.get(0));
    visited[group.get(0)];
    int cnt = 1;
    
    while (!q.isEmpty()) {
    	int cur = q.poll();
        for (int link : adjList.get(cur)) {
        	if (group.contains(link) && !visited[cur]) {
            	visited[link] = true;
                q.add(link);
                cnt++;
            }
        }
    }
    
    return group.size() == cnt;
}

인접리스트 adjList 안의 값을 확인하며 group 내 연결요소가 없는지 확인한다. 연결 되어 있다면 cnt를 하나씩 올려주어

모두 연결 되어 있다면 group의 크기와 cnt가 같을 것이고, 이 경우 true를 반환한다.

다르다면 false!

 

세 번째,  인구 수의 차를 구해 최솟값으로 갱신! ... 해 주는 것은 전체 코드에서 확인해보자!

코드 💡

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {
    static int N;
    static int[] people;
    static StringTokenizer st;
    static ArrayList<ArrayList<Integer>> adjList;
    static int ans = Integer.MAX_VALUE;
    static boolean divChk;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        N = Integer.parseInt(br.readLine());

        people = new int[N + 1];
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            people[i] = Integer.parseInt(st.nextToken());
        }

        adjList = new ArrayList<>();
        adjList.add(new ArrayList<>());
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            int adjCnt = Integer.parseInt(st.nextToken());
            ArrayList<Integer> adj = new ArrayList<>();
            for (int j = 0; j < adjCnt; j++) {
                adj.add(Integer.parseInt(st.nextToken()));
            }
            adjList.add(adj);
        }

        //구역 나누기
        for (int i = 1; i <= N/2; i++) {
            boolean[] visited = new boolean[N + 1];
            combine(1, i, new ArrayList<>(), visited);
        }

        if (divChk) {
            bw.write(ans + "");
        }
        else {
            bw.write(-1 + "");
        }
        br.close();
        bw.flush();
        bw.close();
    }
    public static void combine(int start, int end, ArrayList<Integer> groupA, boolean[] visited) {

        if (groupA.size() == end) {
            ArrayList<Integer> groupB = new ArrayList<>();
            for (int i = 1; i <= N; i++) {
                if (!visited[i]) {
                    groupB.add(i);
                }
            }
            // 그룹이 나뉘었을 때
            if (isLinked(groupA) && isLinked(groupB)) { //두 그룹 모두 인접인 경우
                int sum_A = 0, sum_B = 0;
                for (int i = 0; i < groupA.size(); i++) {
                    sum_A += people[groupA.get(i)];
                }
                for (int i = 0; i < groupB.size(); i++) {
                    sum_B += people[groupB.get(i)];
                }
                ans = Math.min(ans, Math.abs(sum_A - sum_B));
                divChk = true;
            }
            return;
        }

        for (int i = start; i <= N; i++) {
            visited[i] = true;
            groupA.add(i);
            combine(i + 1, end, groupA, visited);
            groupA.remove(groupA.size() - 1);
            visited[i] = false;
        }
    }
    public static boolean isLinked(ArrayList<Integer> group) {
        boolean[] visited = new boolean[N + 1];
        Queue<Integer> q = new LinkedList<>();
        q.add(group.get(0));
        visited[group.get(0)] = true;
        int cnt = 1;

        while (!q.isEmpty()) {
            int cur = q.poll();
            for (int link : adjList.get(cur)) {
                if (!visited[link] && group.contains(link)) {
                    cnt++;
                    q.add(link);
                    visited[link] = true;
                }
            }
        }
        return cnt == group.size();
    }
}

combine 메서드 안에서 정답을 구하는 과정을 확인 해보자!

 

 

알고리즘 자체는 어렵지 않은데.. 두 구역으로 나누는 방법을 떠올리지 못해 오래 걸렸다..

SW역량테스트 A형 어려워 ㅠㅠ

'Algorithm' 카테고리의 다른 글

[TIP💡] 인텔리제이(Intellij)로 코딩테스트 환경 구성하기 (입출력을 txt로?)  (0) 2024.10.02
[백준] 13418 학교 탐방하기 - JAVA  (0) 2024.09.22
[SWEA] 2477 차량 정비소 - JAVA  (0) 2024.09.06
[알고리즘] 비트 마스킹으로 '부분 집합' 구해보기 - JAVA  (0) 2024.08.24
[백준] 14502 연구소 - JAVA  (0) 2024.08.01
'Algorithm' 카테고리의 다른 글
  • [백준] 13418 학교 탐방하기 - JAVA
  • [SWEA] 2477 차량 정비소 - JAVA
  • [알고리즘] 비트 마스킹으로 '부분 집합' 구해보기 - JAVA
  • [백준] 14502 연구소 - JAVA
의중
의중
  • 의중
    개발어려워
    의중
  • 전체
    오늘
    어제
    • 전체글 (30)
      • Algorithm (12)
      • SSAFY (1)
      • 자료구조 (4)
      • 운영체제 (2)
      • JAVA (2)
      • ML, DL (0)
      • BackEnd (2)
      • DevOps (1)
      • 네트워크 (3)
      • DB, SQL (3)
  • hELLO· Designed By정상우.v4.10.3
의중
[백준] 17471 게리 맨더링 - JAVA
상단으로

티스토리툴바