4 분 소요

1. 문제 접근 방법 및 아이디어

이 문제는 모든 가능한 햄스터의 배치 중 조건을 만족하면서 합이 최대인 경우를 찾는 것이 핵심입니다.

  1. 배치 생성
    • 각 우리(1 ~ N)에 들어갈 수 있는 햄스터 수는 0마리부터 X까지 입니다.
  2. 모든 경우의 수 계산
    • 우리 개수(N)가 최대 6개, 햄스터 최대 수(X)가 10이므로, 한 우리당 11가지 경우가 가능합니다.
    • 전체 경우의 수는 11^6이므로, 완전 탐색이 가능합니다.
  3. 조건 검증
    • 기록된 M개의 조건(l번부터 r번 우리까지의 합이 s)을 모두 만족하는지 확인합니다.
  4. 최적화 갱신
    • 조건을 만족하는 배치 중 햄스터 총합이 최대인 것을 찾습니다.
    • 합이 같다면 사전순으로 앞선 것을 선택합니다.


2. 필요 개념 및 알고리즘

  1. 중복 순열
    • 각 우리에 0부터 X까지의 숫자를 중복해서 배치해야 하기 때문입니다.
    • N과 X의 범위가 최대 6개, 햄스터 최대 수 10이기 때문에 지수 시간 복잡도를 갖는 알고리즘을 사용해도 된다는 강력한 신호입니다.
  2. 조건 검증
    • M개의 제한 사항을 모두 통과해야 정답 후보가 됩니다.


3. 어려웠던 점

  1. 문제 의도 파악
    • 중복 순열 문제인지 파악하지 못했습니다.
  2. 배열 복사
    • result = numbers로 하면 주소값만 복사되어 값이 게속 변합니다.
    • 배열 복사 방법인 numbers.clone()을 생각하지 못했습니다.
  3. 배치 불가능 처리
    • 조건을 만족하는 경우가 단 하나도 없을 때 -1을 출력하는 예외 처리를 잊으면 안됩니다.


4. 코드 풀이

4.1 모든 경우의 수 계산

for (int i = 0; i <= X; i++) {
	numbers[level] = i;
	dfs(level + 1, cursum + i);
}
  • 각 우리(1 ~ N)에 들어갈 수 있는 햄스터 수는 0마리부터 X까지 입니다.
  • 우리 개수(N)가 최대 6개, 햄스터 최대 수(X)가 10이므로, 한 우리당 11가지 경우가 가능합니다.

4.2 조건 검증

// 모든 조건 체크하는 함수
static boolean ischeck() {

	// 모든 조건 탐색
	for (int[] it : list) {
		int l = it[0];
		int r = it[1];
		int s = it[2];

		// 햄스터의 총 수
		int sum = 0;

		// l번 우리에서 r번 우리까지의 햄스터의 수 계산
		for (int idx = l; idx <= r; idx++) {
			sum += numbers[idx];
		}

		// 기록에 일치하지 않으면 false 리턴
		if (sum != s)
			return false;
	}

	return true;
}
  • 기록된 M개의 조건(l번부터 r번 우리까지의 합이 s)을 모두 만족하는지 확인합니다.

4.3 최적화 갱신

if (level == N + 1) {
	if (ischeck()) {
	    // 햄스터 수가 가장 많은 것을 갱신한다.
	    if (cursum > maxcount) {
		    maxcount = cursum;
		    result = numbers.clone();
	    }   

	}
    return;
}
  • 조건을 만족하는 배치 중 햄스터 총합이 최대인 것을 찾습니다.
  • 합이 같다면 사전순으로 앞선 것을 선택합니다.
    • 0 ~ X 까지 오름차순으로 DFS를 돌리면 자연스럽게 가장 먼저 발견된 최대합 케이스가 사전순 최적해가 됩니다.

4.4 DFS 함수 코드

// 햄스터 배치 함수
static void dfs(int level, int cursum) {

	// 종료 조건
	if (level == N + 1) {
	    if (ischeck()) {
	        // 햄스터 수가 가장 많은 것을 갱신한다.
	        if (cursum > maxcount) {
		        maxcount = cursum;
		        result = numbers.clone();
	        }   

	    }
        return;
    }

	// 중복 순열
	for (int i = 0; i <= X; i++) {
		numbers[level] = i;
		dfs(level + 1, cursum + i);
	}
}
  • 이전 문제에서 풀었을 때는 cursum을 매개변수로 갖지 않고, 종료 조건에 도달하면 햄스터의 총합을 계산했습니다.
  • 매개변수를 사용하면 연산의 시간을 줄일 수 있습니다.

4.5 전체 코드

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

public class Solution_D4_8275_햄스터 {

	// 우리의 개수, 최대 햄스터의 수, 경근이의 기록의 수
	static int N, X, M;

	// l번 우리, r번 우리, 목표 햄스터 수
	static int l, r, s;

	static int[] numbers; // 우리 속 햄스터 수 모음
	static int maxcount; // 가장 많은 햄스터 수
	static int[] result; // 우리에 있는 햄스터 수
	static ArrayList<int[]> list; // 기록 리스트

	public static void main(String[] args) throws NumberFormatException, IOException {
		// TODO Auto-generated method stub
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();

		int T = Integer.parseInt(br.readLine().trim());

		// tc 수행
		for (int tc = 1; tc <= T; tc++) {

			// 우리의 개수, 최대 햄스터의 수, 경근이의 기록의 수 입력
			StringTokenizer st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken());
			X = Integer.parseInt(st.nextToken());
			M = Integer.parseInt(st.nextToken());

			// 초기화
			numbers = new int[N + 1];
			maxcount = Integer.MIN_VALUE;
			result = null;
			list = new ArrayList<int[]>();

			// l번 우리, r번 우리, 목표 햄스터 수
			for (int i = 0; i < M; i++) {
				st = new StringTokenizer(br.readLine());
				l = Integer.parseInt(st.nextToken());
				r = Integer.parseInt(st.nextToken());
				s = Integer.parseInt(st.nextToken());

				// list 추가
				list.add(new int[] { l, r, s });
			}

			// 처리
			dfs(1, 0);

			// 출력 내용 추가
			sb.append("#" + tc + " ");

			// 햄스터 배치가 불가능하면 -1을 추가
			// 햄스터 배치가 가능하면 1~N번 우리의 순서대로 우리에 있는 햄스터 수를 추가
			if (result == null) {
				sb.append("-1");
			} else {
				for (int i = 1; i <= N; i++) {
					sb.append(result[i] + " ");
				}
			}

			sb.append("\n");

		} // tc 종료

		// 출력
		System.out.println(sb);
	} // 메인 종료

	// 햄스터 배치 함수
	static void dfs(int level, int cursum) {

		// 종료 조건
		if (level == N + 1) {

			if (ischeck()) {

				// 햄스터 수가 가장 많은 것을 갱신한다.
				if (cursum > maxcount) {
					maxcount = cursum;
					result = numbers.clone();
				}

			}

			return;
		}

		// 중복 순열
		for (int i = 0; i <= X; i++) {
			numbers[level] = i;
			dfs(level + 1, cursum + i);
		}

	}

	// 모든 조건 체크하는 함수
	static boolean ischeck() {

		// 모든 조건 탐색
		for (int[] it : list) {
			int l = it[0];
			int r = it[1];
			int s = it[2];

			// 햄스터의 총 수
			int sum = 0;

			// l번 우리에서 r번 우리까지의 햄스터의 수 계산
			for (int idx = l; idx <= r; idx++) {
				sum += numbers[idx];
			}

			// 기록에 일치하지 않으면 false 리턴
			if (sum != s)
				return false;

		}

		return true;
	}

}

카테고리:

업데이트: