728x90
SMALL
자세한 풀이: 주석 참고
두 구획에 따른 가중치의 차이는 없으므로
부분집합의 원소의 수가 1이상 n/2 이하인 조합을 고른다.
고른 조합 두개가 나오면, 각각의 조합(red, blue) 에 대해 union-find를 진행한다.
이때 같은 색에 포함되며 인접해있다면 union을 진행한다.
모든 union이 완료되면 각 노드에 대해 탐색을 진행한다.
1. 모두 방문할 수 있는지,
2. 방문했을 때 색이 같다면(union 되어있다면) 같은 부모에 포함되었는지 검사한다.
package com;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class boj17471 {
static int n;
static int[] population;
static List<Integer>[] adj;
static int[] parent;
static int ans = Integer.MAX_VALUE;
static boolean[] visit;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine());
population = new int[n+1];
adj = new ArrayList[n+1];
parent = new int[n + 1];
visit = new boolean[n + 1];
for (int i = 1; i <= n; i++) {
population[i] = Integer.parseInt(st.nextToken());
adj[i] = new ArrayList<>();
}
for (int i = 1; i <= n; i++) {
st = new StringTokenizer(br.readLine());
int num = Integer.parseInt(st.nextToken());
for (int j = 1; j <= num; j++) {
int a = Integer.parseInt(st.nextToken());
adj[i].add(a);
adj[a].add(i); // 양방향 저장
}
}
for (int i = 0; i < (1 << n); i++) {
int cnt = 0;
for (int j = 0; j < n; j++) {
if ((i & (1 << j)) != 0) cnt++;
}
List<Integer> red = new ArrayList<>();
List<Integer> blue = new ArrayList<>();
if (cnt > 0 && cnt < (n / 2) + 1) { // 1개 이상, n/2개 이하의 수로 구성된 red, blue 조합
for (int j = 0; j < n; j++) {
if ((i & (1 << j)) != 0) red.add(j + 1);
}
for (int j = 1; j <= n; j++) {
if (!red.contains(j)) {
blue.add(j);
}
}
for (int j = 0; j <= n; j++) {
parent[j] = j;
}
// union-find를 진행하는 gary 함수
// 같은 색이고, 인접하다면 union한다.
gary(red); // 게리맨더링 진행 (각 구역의 조상이 서로 같은지)
gary(blue);
int redTotal = 0;
int blueTotal = 0;
boolean flag = true;
for (int j = 1; j <= n; j++) {
visit[j] = false; // 조합마다 bfs 진행하므로 방문 배열 초기화
}
if (bfs(red, red.get(0)) && bfs(blue, blue.get(0))) {
for (int j = 1; j <= n; j++) {
if (!visit[j]) {
flag = false;
break;
}
}
if (!flag) continue;
for (int r : red) {
redTotal += population[r];
}
for (int b : blue) {
blueTotal += population[b];
}
ans = Math.min(ans, Math.abs(redTotal - blueTotal));
}
}
}
if (ans == Integer.MAX_VALUE) {
ans = -1;
}
System.out.println(ans);
}
static int getParent(int n1) {
if (parent[n1] == n1) return n1;
else return getParent(parent[n1]);
}
static void unionParent(int n1, int n2) {
int p1 = getParent(n1);
int p2 = getParent(n2);
if (p1 < p2) parent[p2] = p1;
else parent[p1] = p2;
}
static void gary(List<Integer> color) { // 같은 색 배열의 인덱스이고 인접하다면 같은 구역으로 묶는다
for (int cur : color) {
for (int i = 0; i < adj[cur].size(); i++) {
int nxt = adj[cur].get(i);
if (!color.contains(nxt)) continue; // 같은 구역 색깔이 아니라면 합치지 않는다.
unionParent(cur, nxt);
}
}
}
static boolean bfs(List<Integer> color, int c) { // bfs check
// 모든 노드 탐색 - bfs 진행
// 노드가 서로 연결되어 있고 같은 구역이라면 true, 아니면 false 반환
Queue<Integer> q = new ArrayDeque<>();
q.add(c);
visit[c] = true;
while (!q.isEmpty()) {
int cur = q.poll();
visit[cur] = true;
for (int i = 0; i < adj[cur].size(); i++) {
int nxt = adj[cur].get(i);
if (!color.contains(nxt)) continue; // 같은 구역 색이 아니라면 검사하지 않는다.
if (visit[nxt]) continue;
int p1 = getParent(cur);
int p2 = getParent(nxt);
if (p1 != p2) return false;
q.add(nxt);
}
}
return true;
}
}
옛날엔 못 푼 문젠데 풀려서 기쁘다 ^-^
728x90
LIST
'PS > BOJ' 카테고리의 다른 글
경로압축의 중요성... (0) | 2024.10.30 |
---|---|
백준 1992 (Java) 쿼드트리 (0) | 2023.02.21 |
백준 1753 (Java) 최단 경로 (0) | 2023.02.17 |
백준 18352 (Java) 특정 거리의 도시 찾기 (0) | 2023.02.16 |
백준 16935 (Java) 배열 돌리기 3 (0) | 2023.02.15 |