[문제링크]

 

17471번: 게리맨더링

선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.

www.acmicpc.net

 

0. 인구의 수는 항상 양수이므로, 모든 구역이 한쪽 선거구에 속할 경우는 고려하지 않아도 된다

  - N:0의 인구의 차이는, N-1:1의 인구의 차이보다 항상 클 테니까

 

1. 모든 선거구의 부분집합 S을 구한다(sub함수)

 

2. S에 속한 한 선거구, S에 속하지 못한 한 선거구를 선택해, 각각 bfs를 실행한다

  - bfs를 돌며 선거구의 개수와 그 인구수를 누적한다

 

3. 두 bfs의 결과 선거구의 합이 전체 선거구의 합과 같다면, 잘 연결된 두개의 선거구가 존재한다는 것

  - 누적한 인구의 차이를 반환한다

 

4. 3 결과값의 최종 최솟값을 출력한다

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main{
	
	static int N, min;
	static int[]people;
	static boolean[][]adj;
	static Queue<Integer>pick = new LinkedList<Integer>();
	static Queue<Integer>nonpick = new LinkedList<Integer>();
	
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		N = pint(br.readLine());
		min=Integer.MAX_VALUE;
		people=new int[N];
		adj=new boolean[N][N];
		
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		for (int i = 0; i < N; i++) people[i] = pint(st.nextToken());
		
		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			st.nextToken();
			while(st.hasMoreTokens()) adj[i][pint(st.nextToken())-1]=true;
		}
		
		sub(0, 0);
		System.out.println(min==Integer.MAX_VALUE? -1 : min);
		
	}
	
	static void sub(int cnt, int mask) {
		if(cnt==N) {
			//mask : 선택한 노드
			min=Math.min(bfs(mask), min);
			return;
		}
		sub(cnt+1, mask|1<<cnt);
		sub(cnt+1, mask);
	}
	
	static int bfs(int mask) {
		//bfs두번 실행, 모든 노드를 탐색하는가?
		int s1=-1, s2=-1;
		for (int i = 0; i < N; i++) {
			if((mask&1<<i)!=0)s1=i; // 1인 노드 중 아무거나
			else s2=i; // 0인 노드 중 아무거나
		}
		
		int cnt1=0, total1=0, temp=mask;
		if(s1!=-1) {
			pick.offer(s1);
			temp^=1<<s1; // 방문 노드 삭제
			total1+=people[s1];
			cnt1++;
		}
		while(!pick.isEmpty()) {
			int cur = pick.poll();
			
			for (int i = 0; i < N; i++) {
				//가본적 없고, 연결되있으면
				if((temp&1<<i)!=0 && adj[cur][i]) {
					pick.offer(i);
					temp^=1<<i; // 방문 노드 삭제
					total1+=people[i];
					cnt1++;
				}
			}
		}
		
		int cnt2=0, total2=0; temp=mask;
		if(s2!=-1) {
			nonpick.offer(s2);
			temp^=1<<s2; // 방문 노드 삭제
			total2+=people[s2];
			cnt2++;
		}
		while(!nonpick.isEmpty()) {
			int cur = nonpick.poll();
			
			for (int i = 0; i < N; i++) {
				//가본적 없고, 연결되있으면
				if((temp&1<<i)==0 && adj[cur][i]) {
					nonpick.offer(i);
					temp^=1<<i; // 방문 노드 삭제
					total2+=people[i];
					cnt2++;
				}
			}
		}
		if(cnt1+cnt2==N)return Math.abs(total1-total2);
		else return Integer.MAX_VALUE;
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

'알고리즘 문제 풀이 > BOJ 골드' 카테고리의 다른 글

[백준] 1238 - 파티  (0) 2021.04.18
[백준] 1967 - 트리의 지름  (0) 2021.04.18
[백준] 2263 - 트리의 순회  (0) 2021.04.16
[백준] 4056 - 스-스-스도쿠  (0) 2021.04.16
[백준] 2239 - 스도쿠  (0) 2021.04.16
[백준] 2096 - 내려가기  (0) 2021.04.16
[백준] 15961 - 회전 초밥  (0) 2021.04.15

+ Recent posts