1. 문제

2. 접근방법

부분집합으로 가능한 첫번째 선거구를 구하고 골라지지 않은 다른 지역들을 두번째 선거구로 뽑은다음

각 선거구들이 모두 이어져있는지 검사한 뒤

둘 다 잘 이어져 있다면 각 선거구의 인원 합의 차를 구해 최솟값을 구하는 문제

 

이 문제도 할것이 굉장히 많다.

 

우선 부분집합을 비트마스킹을 활용해 구하였다. (부분집합을 비트마스크로 뽑는 것은 구글링 ㄱㄱ)

비트 마스킹을 사용하면 (모든구역 - 뽑힌 비트마스크)를 하면 쉽게 두번째 선거구 역시 구할 수 있다.

이 상황을 비트마스크로 표현하면

첫번째 뽑은 선거구

001101

 

전체 선거구

-첫번째 뽑은 선거구

-----------------------

= 두번째 뽑은 선거구

 

 111111

-001101

-------------

110010

 

그리고 뽑을 때

123 뽑고 456 뽑으나

456 뽑고 123 뽑으나 같으니

 

부분집합을 절반까지만 뽑으면 된다.

 

 

이 후 뽑힌 두 비트마스크를 bfs로 다 연결 되어있는지 검사하고

두 선거구의 인원들의 차를 구하면 되겠다.

 
3. 풀이

1. 입력을 인접행렬로 받아오기 (인접리스트도 괜찮다)

2. 비트마스크를 1부터 ((1 << N) - 1)/2  까지 1씩 증가하며 뽑기 //하나도 안뽑히는 경우는 없다고 하였으므로

3. ((1 << N)-1 - 첫번째선거구비트)로 두번째 선거구 뽑기

4. bfs돌며 각 선거구 다 이어졌는지 검사

5. 둘다 이어져 있으면 인원수 구하고 인원수의 차이를 구하기

6. min보다 작으면 min에 저장

4. 자바 코드

package Gold;

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

public class P17471게리멘더링 {
	static int people[];
	static int N;
	static boolean matrix[][];
	static int min = 123456789;
	public static void main(String[] args) throws IOException {
		// 모든 부분 집합을 비트마스크로 뽑는다.
		// 모든구역 - 뽑은 비트마스크 하면 반대편 선거구도 뽑힌다.
		// 두 구역 모두 연결 되어있는지 bfs검사
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		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());

		matrix = new boolean[N + 1][N + 1];
		for (int i = 1; i <= N; i++) { //입력 받기
			st = new StringTokenizer(br.readLine());
			int etc = Integer.parseInt(st.nextToken());
			for (int j = 0; j < etc; j++) {
				int target = Integer.parseInt(st.nextToken());
				matrix[i][target] = true;
				matrix[target][i] = true;
			}
		}

		for (int i = 1; i <= ((1 << N) - 1) / 2; i++) {
			ArrayList<Integer> section_1 = makeSection(i);
			if (!check(section_1))
				continue;
				
			
			ArrayList<Integer> section_2 = makeSection((1 << N)-1 - i);
			if (!check(section_2))
				continue;
			
			int diff = Math.abs(getPeople(section_1) - getPeople(section_2));
			min = Math.min(diff, min);
		}
		if(min == 123456789)
			min = -1;
		System.out.println(min);

	}

	static ArrayList<Integer> makeSection(int Section) {
		ArrayList<Integer> section = new ArrayList<Integer>();
		for (int i = 0; i < N; i++) {
			if ((Section & (1 << i)) != 0)
				section.add(i+1);
		}
		return section;
	}
	static boolean check(ArrayList<Integer> originSection) {
		ArrayList<Integer> section = (ArrayList<Integer>) originSection.clone(); 
		Queue<Integer> que = new LinkedList<Integer>();
		que.offer(section.get(0));
		section.remove(0);
		while (!que.isEmpty()) {
			int now = que.poll();
			for (int i = section.size() - 1; i >= 0; i--) {
				if (matrix[now][section.get(i)]) {
					que.offer(section.get(i));
					section.remove(i);
				}
			}
		}
		if(section.size() == 0)
			return true;
		return false;
	}

	static int getPeople(ArrayList<Integer> section) {
		int sum = 0;
		for(int i : section)
			sum += people[i];
		return sum;
	}
}

5. 마치며

이 문제가 삼성 A형 기출이라던데

기본 개념들은 어렵지 않으나 여러 개념이 섞여 있어 체력 기르기에 좋은 문제였다.

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[백준] 19535 ㄷㄷㄷㅈ  (0) 2020.08.31
[백준] 18513 샘터  (0) 2020.08.31
[백준] 2206 벽 부수고 이동하기  (0) 2020.08.26
[백준] 18808 스티커 붙이기  (0) 2020.08.23
[백준] 2573 빙산  (0) 2020.08.23

+ Recent posts