게리맨더링


백준 [17471] 게리맨더링

문제보기
Alt text

소스코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	static int N, ans;
	static int[] popul;
	static int[][] adj;

	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		N = Integer.parseInt(br.readLine());
		ans = 987654321;
		popul = new int[N];
		adj = new int[N][N];

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

		for (int i = 0; i < N; i++) {
			st = new StringTokenizer(br.readLine());
			int cnt = Integer.parseInt(st.nextToken());
			for (int j = 0; j < cnt; j++) {
				int area = Integer.parseInt(st.nextToken()) - 1;
				adj[i][area] = 1;
				adj[area][i] = 1;
			}
		}

		pick(new boolean[N], 0, 0);
		if (ans == 987654321)
			ans = -1;
		System.out.println(ans);
	}

	public static void pick(boolean[] sel, int idx, int cnt) {
		if (idx == sel.length) {
			if (cnt != 0 && cnt != N)
				chk(sel);
			return;
		}
		sel[idx] = false;
		pick(sel, idx + 1, cnt + 1);
		sel[idx] = true;
		pick(sel, idx + 1, cnt);
	}

	public static void chk(boolean[] sel) {
		int flagF = -1;
		int flagT = -1;
		int cntF = 0;
		int cntT = 0;
		boolean result = true;
		for (int i = 0; i < N; i++) {
			flagChk = false;
			if (!sel[i]) {
				if (flagF < 0) {
					flagF = i;
					cntF += popul[i];
					continue;
				} else {
					// 이어져있는지 체크
					isConnect(new boolean[N][N], sel, i, flagF, false);
					if (!flagChk) {
						result = false;
						break;
					}
					flagF = i;
					cntF += popul[i];
				}
			} else {
				if (flagT < 0) {
					flagT = i;
					cntT += popul[i];
					continue;
				} else {
					isConnect(new boolean[N][N], sel, i, flagT, true);
					if (!flagChk) {
						result = false;
						break;
					}
					flagT = i;
					cntT += popul[i];
				}
			}
		}
		if (result) {
			int diff = Math.abs(cntF - cntT);
			ans = Math.min(ans, diff);
		}
	}

	static boolean flagChk;

	public static void isConnect(boolean[][] v, boolean[] sel, int i, int flag, boolean chk) {
		// 가는길에 반대 flag 만나면 연결될 수 없다.
		for (int k = 0; k < N; k++) {
			if (v[i][k] || adj[i][k] == 0 || sel[k] != chk)
				continue;
			if (adj[i][k] == 1) {
				if (k == flag) {
					flagChk = true;
					break;
				} else {
					v[i][k] = true;
					v[k][i] = true;
					isConnect(v, sel, k, flag, chk);
				}
			}
		}
	}
}