본문 바로가기

문제 풀이/Baekjoon

[백준] G4 2617번 구슬 찾기 (JAVA)

문제 출처 - Baekjoon Online Judge

문제는 여기

 

2617번: 구슬 찾기

모양은 같으나, 무게가 모두 다른 N개의 구슬이 있다. N은 홀수이며, 구슬에는 번호가 1,2,...,N으로 붙어 있다. 이 구슬 중에서 무게가 전체의 중간인 (무게 순서로 (N+1)/2번째) 구슬을 찾기 위해서

www.acmicpc.net

[문제] 

모양은 같으나, 무게가 모두 다른 N개의 구슬이 있다. N은 홀수이며, 구슬에는 번호가 1,2,...,N으로 붙어 있다. 이 구슬 중에서 무게가 전체의 중간인 (무게 순서로 (N+1)/2번째) 구슬을 찾기 위해서 아래와 같은 일을 하려 한다.

우리에게 주어진 것은 양팔 저울이다. 한 쌍의 구슬을 골라서 양팔 저울의 양쪽에 하나씩 올려 보면 어느 쪽이 무거운가를 알 수 있다. 이렇게 M개의 쌍을 골라서 각각 양팔 저울에 올려서 어느 것이 무거운가를 모두 알아냈다. 이 결과를 이용하여 무게가 중간이 될 가능성이 전혀 없는 구슬들은 먼저 제외한다.

예를 들어, N=5이고, M=4 쌍의 구슬에 대해서 어느 쪽이 무거운가를 알아낸 결과가 아래에 있다.

  1. 구슬 2번이 구슬 1번보다 무겁다.
  2. 구슬 4번이 구슬 3번보다 무겁다.
  3. 구슬 5번이 구슬 1번보다 무겁다.
  4. 구슬 4번이 구슬 2번보다 무겁다.

위와 같이 네 개의 결과만을 알고 있으면, 무게가 중간인 구슬을 정확하게 찾을 수는 없지만, 1번 구슬과 4번 구슬은 무게가 중간인 구슬이 절대 될 수 없다는 것은 확실히 알 수 있다. 1번 구슬보다 무거운 것이 2, 4, 5번 구슬이고, 4번 보다 가벼운 것이 1, 2, 3번이다. 따라서 답은 2개이다.

M 개의 쌍에 대한 결과를 보고 무게가 중간인 구슬이 될 수 없는 구슬의 개수를 구하는 프로그램을 작성하시오.

[입력]

첫 줄은 구슬의 개수를 나타내는 정수 N(1 ≤ N ≤ 99)과 저울에 올려 본 쌍의 개수 M(1 ≤ M ≤ N(N-1)/2)이 주어진다. 그 다음 M 개의 줄은 각 줄마다 두 개의 구슬 번호가 주어지는데, 앞 번호의 구슬이 뒤 번호의 구슬보다 무겁다는 것을 뜻한다.

[출력]

첫 줄에 무게가 중간이 절대로 될 수 없는 구슬의 수를 출력 한다.

 


[풀이]

1. 값을 입력받아주고 [큰 구슬][작은 구슬]에는 1을 [작은 구슬][큰 구슬]에는 -1을 담아준다.

2. 플로이드 와샬을 사용해 출발>경유, 경유>도착의 크기가 같고 도착>시작의 크기가 0이 아닌 경우 출발>도착을  출발 >경유 값을 넣는다.

3. 플로이드 와샬을 통해 자기 자신을 기준으로 더 큰 구슬과 더 작은 구슬을 구할 수 있다.

4. 이를 사용해 큰 구슬의 개수와 작은 구슬의 개수를 구해준다.

5. 큰 구슬과 작은 구슬의 개수가 반보다 큰 경우 정답 값을 증가시켜준다.

6. 결과를 출력해준다.

[접근]

1. DFS로 문제를 해결하려고 하였다.

2. 하지만 플로이드 와샬을 사용하면 되겠다고 생각하여 플로이드 와샬을 사용해 해결하였다.

[코드]

import java.io.*;
import java.util.*;

public class Main {
	static int n, m;
	static int[][] arr;
	static int mid, ans;

	public static void main(String[] args) throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		
		arr = new int[n + 1][n + 1];
		
		// 값을 입력받기
		for (int i = 0; i < m; i++) {
			st = new StringTokenizer(br.readLine());
			
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			
			arr[a][b] = 1; // 큰 구슬은 1
			arr[b][a] = -1; // 작은 구슬은 -1
		}
		
		for (int i = 1; i <= n; i++) { // 경유지
			for (int j = 1; j <= n; j++) { // 출발지
				// 자기 자신의 경우 탐색할 필요 없음
				if (i == j)
					continue;
				
				for (int k = 1; k <= n; k++) { // 도착지
					// 같은 경우 탐색할 필요가 없음
					if (i == k || k == j)
						continue;
					
					// 출발>경유, 경유>도착의 크기가 같고 도착>시작의 크기가 0이 아닌 경우 
					if (arr[k][i] != 0 && arr[j][i] == arr[i][k])
						arr[j][k] = arr[j][i]; // 출발>도착 = 출발 >경유
				}
			}
		}
		
		int[] bigger = new int[n + 1];
		int[] smaller = new int[n + 1];
		
		// 기준 구슬보다 작아야하는 갯수, 커야하는 갯수를 구하기
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				// 큰 구슬인 경우
				if (arr[i][j] == 1)
					bigger[i]++;
				// 작은 구슬인  경우
				if (arr[i][j] == -1)
					smaller[i]++;
			}
		}
		
		// 중간값
		mid = (n / 2) + 1;
		
		// 중간값보다 많은 경우 중간값이 될 수 없으므로 정답수 증가
		for (int i = 1; i <= n; i++) {
			//중간값보다 많은 경우
			if (bigger[i] >= mid)
				ans++;
			// 중간값보다 많은 경우 
			if (smaller[i] >= mid)
				ans++;
		}
		
		System.out.println(ans);
	}
}