행렬 N개가 있을 때 적절히 순서를 바꿔 곱을 할 경우 연산 횟수의 최소를 구하는 문제이다.

 

예를 들어 행렬 A(a,b), B(b,c), C(c,d)가 있을 때 곱하는 경우는 두 가지가 있을 수 있다.

  • (AB)C 일 경우 연산 횟수 a*b*c + a*c*d = a*c*(b+d)
  • A(BC) 일 경우 연산 횟수 b*c*d + a*b*d = b*d*(a+c)

 

행렬 곱을 구할 때, 이전에 구한 값을 재사용 할 가능성이 있기 때문에 dp로 문제를 푸는 방향으로 생각해 봤다.

dp[i][j]: i~j까지 행렬까지의 곱 중 가장 적은 연산 횟수라고 정의한다면,

i<=k<=j인 k에 대해 dp[i][k] + dp[k][j] + i*k*j 중 최소값이 dp[i][j]가 될 것이다.

class Solution {
	public int solution(int[][] matrix_sizes) {
		int length = matrix_sizes.length;

		Matrix matrix[] = new Matrix[length + 1];
		for (int i = 1; i <= length; i++) {
			matrix[i] = new Matrix(matrix_sizes[i - 1][0], matrix_sizes[i - 1][1]);
		}
		int dp[][] = new int[length + 1][length + 1];

		for (int i = length; i > 0; i--) {
			for (int j = i + 1; j <= length; j++) {
				dp[i][j] = Integer.MAX_VALUE;
				for (int k = i; k < j; k++) {
					dp[i][j] = Math.min(dp[i][j],
							dp[i][k] + dp[k + 1][j] + (matrix[i].row * matrix[k].col * matrix[j].col));
				}
			}
		}

		return dp[1][length];
	}
}

class Matrix {
	int row, col;

	public Matrix(int row, int col) {
		this.row = row;
		this.col = col;
	}
}

모든 수가 0 또는 1로 이루어진 2차원 배열 a가 주어질 때, 2차원 배열 b의 경우의 수를 (10^7 + 19)로 나눈 나머지를 구하는 문제이다.

 

문제 조건

  • b의 모든 원소는 0 또는 1이며 a와 b의 크기가 같다
  • a의 i번째 열과 b의 i번째 열에 들어 있는 1의 개수가 같다.
  • b의 각 행에 들어 있는 1의 개수가 짝수이다. (0도 짝수)

일단... 경우의 수를 ~~로 나눈 나머지를 구하라고 했으니 이 수가 엄청나게 클 것이고 그냥 구하면 시간 초과가 날 것이라는 것을 알 수 있다.

대개 이렇게 결과값의 나머지를 구하라는 문제는 dp로 푸는데, dp를 어떻게 적용하면 좋을지 생각해 봤다.

 

dp[i][j]: (0, 0) ~ (i, j)까지 조건에 맞는 b를 만들 수 있는 경우의 수라고 정의하자.

 

만약 행들 중 1의 개수가 짝수이면 1을 더하면 홀수가 될 것이고, 홀수이면 1을 더하면 짝수개가 될 것이다.

열 하나를 추가해서 새롭게 만들어지는 배열은 몇 개의 짝수행을 가지고 있을 것인가. 그리고 만들 수 있는 경우의 수는 어떻게 될까.

 

기존에 가지고 있던 짝수행의 개수는 num개 였고 k개의 행에 1을 추가한다고 했기 때문에 k개의 행이 홀수행이 될 것이고 남은 num-k개가 여전히 짝수행일 것이다. 이 경우의 수는 기존 짝수행의 개수 num개 중 k개를 넣을 것이기 때문에 조합 (num)C(k) 이다.

기존에 가지고 있던 홀수행은 n-num개(전체 배열의 행 크기 - 짝수 행 크기) 일 것이다. 조건 2에 의해 입력배열에서  column+1 열의 1의 개수를 알 수 있고 이를 oneCnt라 하자. oneCnt 중 k개는 짝수행에 넣을 것이고 남은 oneCnt - k개의 1을 홀수행에 넣을 것이다. 따라서 oneCnt-k개가 짝수행이 될 것이다. 이 경우의 수는 기존 홀수행의 개수 n-num개 중 oneCnt - k개를 넣을 것이기 때문에 조합 (n-num)C(oneCnt-k)가 될 것이다. 

 

위와 같이 짝수행, 홀수행의 경우를 독립사건으로 보고 각자의 경우의 수를 구했으면 두 경우의 수를 곱한 결과에 dp[column][num]을 곱한 결과가 우리가 구하고자 하는 dp[column+1][(num-k) + (oneCnt-k)] 값이 된다.

 

for(column을 1~m까지)
  for(num을 0에서 n까지)
    for(k를 0에서 oneCnt까지)
       // 점화식
       dp[column+1][(num-k) + (oneCnt-k)] = SUM[dp[column][num] * (num)C(k) * (n-num)C(oneCnt-k)]

설명에서는 생략했지만 dp 배열에 들어가는 계산에는 10^7 + 19 나머지 연산을 해서 변수값 범위를 넘어가지 않도록 주의한다. 

 

시간복잡도는 O(N^2 * M)


소스코드 : github.com/fpdjsns/Algorithm/blob/master/programmers/%EC%9B%94%EA%B0%84%20%EC%BD%94%EB%93%9C%20%EC%B1%8C%EB%A6%B0%EC%A7%80%20%EC%8B%9C%EC%A6%8C1/%EC%A7%9D%EC%88%98%20%ED%96%89%20%EC%84%B8%EA%B8%B0.cpp

문제 조건

  • 사칙연산에서 더하기(+)는 결합법칙이 성립하지만, 빼기(-)는 결합법칙이 성립하지 않는다
  • 짝수번째는 숫자, 홀수번째는 연산자이다
  • 문자열 형태의 숫자와, +, - 가 들어있는 배열의 계산 결과 중 최댓값을 구하는 문제이다

 

모든 경우에 대해 구하되, 구한 값을 어딘가 저장해 두어야 한다. => dp

dp[i][j]: 인덱스 i ~ j 까지 괄호 계산을 한 값이라고 정의하자.

이때, 양수 연산의 경우 뒤에 있는 값이 최대한 커야하고, 음수 연산의 경우 뒤에 있는 값이 최대한 작아야 최댓값을 구할 수 있다.

따라서 양수 연산이 앞에 있을 땐 dp에 최댓값을, 음수 연산이 앞에 있을 땐 dp에 최솟값을 저장하도록 한다.

 

class Solution {
	public int solution(String arr[]) {
		int n = arr.length;
		int[][] dp = new int[n][n];
		
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				dp[i][j] = Integer.MIN_VALUE;
			}
			if (i % 2 == 0) {
				dp[i][i] = Integer.parseInt(arr[i]);
			}
		}
		
		return calculateItoJ(arr, dp, 0, n - 1);
	}

	public static int calculateItoJ(String[] arr, int[][] dp, int i, int j) { // i부터 j까지 연산
		if (dp[i][j] != Integer.MIN_VALUE) {
			return dp[i][j];
		}

		if (i - 1 >= 1 && arr[i - 1].equals("-")) { // - 연산 처리: 뒷부분이 최소가 되어야
			int result = Integer.MAX_VALUE;

			for (int k = i; k < j; k += 2) {
				int subResult = calculate(calculateItoJ(arr, dp, i, k), arr[k + 1], calculateItoJ(arr, dp, k + 2, j));
				result = Math.min(result, subResult);
			}
			dp[i][j] = result;

		} else { // + 연산 처리: 뒷부분이 최대가 되어야
			int result = Integer.MIN_VALUE;

			for (int k = i; k < j; k += 2) {
				int subResult = calculate(calculateItoJ(arr, dp, i, k), arr[k + 1], calculateItoJ(arr, dp, k + 2, j));
				result = Math.max(result, subResult);
			}
			dp[i][j] = result;

		}
		return dp[i][j];
	}

	public static int calculate(int a, String op, int b) {
		if (op.equals("-")) {
			return a - b;
		}
		if (op.equals("+")) {
			return a + b;
		}
		return 0;
	}
}

 

 

문제 조건

레스토랑의 구조는 완전히 동그란 모양이고, 외벽의 총 둘레는 n(미터)이며, 외벽의 몇몇 지점은 취약한 지점들이 있다.

따라서 내부 공사 도중에 주기적으로 점검을 한다. 점검 시간을 1시간으로 제한된다.

 

친구들이 1시간 동안 이동할 수 있는 거리는 제각각이기 때문에, 최소한의 친구들을 투입해 취약 지점을 점검하려고 한다.

편의상 레스토랑의 정북 방향 지점을 0으로 나타내며, 취약 지점의 위치는 정북 방향 지점으로부터 시계 방향으로 떨어진 거리로 나타낸다.

친구들은 출발 지점부터 시계, 혹은 반시계 방향으로 외벽을 따라서만 이동한다.

 

예제는 아래와 같고, 어디서든 출발하면서 특정 시간동안 많은 취약점을 찾도록 하면 되는 것 같다.

n = 12
weak = [1, 5, 6, 10]
dist = [1, 2, 3, 4]
=> answer = 2

https://programmers.co.kr/learn/courses/30/lessons/60062

 

일단 1 <= n <= 200, weak의 길이는 1 이상 15 이하, dist의 길이는 1 이상 8 이하이다.

수의 범위가 굉장히 작아서 완전탐색을 해도 될 것 같다고 생각했다.

 

문제 풀이 과정은 아래와 같다.

1. 일단 모든 경우의 수(순열)을 뽑는다.

2. 순열이 외벽 점검을 만족하는지 확인하고 인원수를 업데이트 해 준다.

  • weak = [1, 5, 6, 10]이라면 [1, 5, 6, 10], [5, 6, 10, 13], [6, 10, 13, 17], [10, 13, 17, 18]의 외벽점검을 시작한다.
  • 적은 수의 순열부터 구한 다음에 min 값이 업데이트 되면 그 뒤는 계산하지 않도록 한다.
import java.util.*;
class Solution {
	public static int min = Integer.MAX_VALUE;

	public int solution(int n, int[] weak, int[] dist) {
		boolean[] isVisited = new boolean[dist.length];
		int[] output = new int[dist.length];

		for (int i = 1; i <= n; i++) {
			permutation(dist, output, isVisited, 0, n, i, weak);
			if (min != Integer.MAX_VALUE) {
				break;
			}
		}

		if (min == Integer.MAX_VALUE) {
			min = -1;
		}
		return min;
	}

	public static void permutation(int[] dist, int[] output, boolean[] visited, int depth, int n, int r, int[] weak) {
		int length = dist.length;

		if (min < r) {
			return;
		}

		if (depth == r) {
			if (isPossible(output, n, r, weak)) {
				min = Math.min(min, r);
			}
			return;
		}

		for (int i = 0; i < length; i++) {
			if (visited[i] != true) {
				visited[i] = true;
				output[depth] = dist[i];
				permutation(dist, output, visited, depth + 1, n, r, weak);
				visited[i] = false;
			}
		}
	}

	public static boolean isPossible(int[] output, int n, int r, int[] weak) {
		if (min <= r) {
			return true;
		}

		int length = weak.length;
		for (int start = 0; start < length; start++) {
			int userIndex = 0;
			int before = weak[start];
			boolean possibleUnit = true;

			for (int i = start; i < start + length; i++) {

				int now = weak[i % length];
				if (i >= length) {
					now += n;
				}
				if (now - before > output[userIndex]) {
					userIndex++;
					before = now;
				}

				if (userIndex >= r) {
					possibleUnit = false;
					break;
				}
			}
			if (possibleUnit) {
				return true;
			}
		}
		return false;
	}
}

 

 

설치 조건

  • 기둥
    • 바닥 위에 있거나 (y=0)
    • 보의 한쪽 끝 부분 위에 있거나 다른 기둥 위에 있어야 한다 (아래와 이어져있다)
    • 한쪽 끝 부분이 기둥 위에 있거나 또는 양쪽 끝 부분이 다른 보와 동시에 연결되어 있어야 한다. (왼쪽이나 오른쪽과 이어져있다)
  • 기둥과 보는 길이가 1인 선분으로 표현된다.

 

예시

https://programmers.co.kr/learn/courses/30/lessons/60061

  1. (1, 0)에서 위쪽으로 기둥을 하나 설치 후, (1, 1)에서 오른쪽으로 보를 하나 만듭니다.
  2. (2, 1)에서 위쪽으로 기둥을 하나 설치 후, (2, 2)에서 오른쪽으로 보를 하나 만듭니다.
  3. (5, 0)에서 위쪽으로 기둥을 하나 설치 후, (5, 1)에서 위쪽으로 기둥을 하나 더 설치합니다.
  4. (4, 2)에서 오른쪽으로 보를 설치 후, (3, 2)에서 오른쪽으로 보를 설치합니다.

만약 (4, 2)에서 오른쪽으로 보를 먼저 설치하지 않고, (3, 2)에서 오른쪽으로 보를 설치하려 한다면 2번 규칙에 맞지 않으므로 설치가 되지 않습니다.

기둥과 보를 삭제하는 기능도 있는데 기둥과 보를 삭제한 후에 남은 기둥과 보들 또한 위 규칙을 만족해야 한다.

만약, 작업을 수행한 결과가 조건을 만족하지 않는다면 해당 작업은 무시된다.

 

 

어떻게 풀지 생각해 보다가 구조물을 추가할 수 있는지 여부를 보는 함수 addValid을 만들어 두고,

구조물을 제거할 때에는 구조물을 일단 제거한 뒤, 주위에 있는 기둥과 보를 다시 추가할 때 조건에 맞는지 여부를 addValid를 이용해서 보도록 했다.

이렇게 풀면 조건에 대해 세세하게 생각할 필요 없이 addValid 함수만 호출하면 되어서 쉽게 문제를 풀 수 있다.

import java.util.*;
class Solution {
	public static int HORIZONTAL = 1;
	public static int VERTICAL = 0;

	public static int ADD = 1;
	public static int DELETE = 0;

	public int[][] solution(int n, int[][] build_frame) {
		TreeSet<Structure> set = new TreeSet<Structure>();

		for (int i = 0; i < build_frame.length; i++) {
			int x = build_frame[i][0];
			int y = build_frame[i][1];
			boolean isVertical = build_frame[i][2] == VERTICAL ? true : false;
			boolean isAdd = build_frame[i][3] == ADD ? true : false;

			if (!isAdd) {
				removeValid(x, y, isVertical, set);
			} else {
				addValid(x, y, isVertical, set);
			}
		}

		int[][] answer = new int[set.size()][3];
		int index = 0;
		for (Structure s : set) {
			answer[index][0] = s.x;
			answer[index][1] = s.y;
			answer[index][2] = s.structure;
			index++;
		}
		return answer;
	}

	public void removeValid(int x, int y, boolean isVertical, TreeSet<Structure> set) {
		if (isVertical) { // 기둥
			if (set.contains(new Structure(x, y + 1, VERTICAL))) {
				// 제거할 기둥 위에 기둥이 있는 경우
				set.remove(new Structure(x, y, VERTICAL));
				if (!addValid(x, y + 1, true, set)) {
					set.add(new Structure(x, y, VERTICAL));
					return;
				}
			}

			if (set.contains(new Structure(x, y + 1, HORIZONTAL))) {
				// 기둥 위에 오른쪽 보가 지탱이 안되는 경우
				set.remove(new Structure(x, y, VERTICAL));
				if (!addValid(x, y + 1, false, set)) {
					set.add(new Structure(x, y, VERTICAL));
					return;
				}
			}

			if (set.contains(new Structure(x - 1, y + 1, HORIZONTAL))) {
				// 기둥 위에 왼쪽 보가 지탱이 안되는 경우
				set.remove(new Structure(x, y, VERTICAL));
				if (!addValid(x - 1, y + 1, false, set)) {
					set.add(new Structure(x, y, VERTICAL));
					return;
				}
			}

			set.remove(new Structure(x, y, VERTICAL));
			return;
		}

		// 보
		if (set.contains(new Structure(x, y, VERTICAL))) {
			// 보 위에 기둥이 있는 경우
			set.remove(new Structure(x, y, HORIZONTAL));
			if (!addValid(x, y, true, set)) {
				set.add(new Structure(x, y, HORIZONTAL));
				return;
			}
		}
		if (set.contains(new Structure(x + 1, y, VERTICAL))) {
			// 보 오른쪽 위에 기둥이 있는 경우
			set.remove(new Structure(x, y, HORIZONTAL));
			if (!addValid(x + 1, y, true, set)) {
				set.add(new Structure(x, y, HORIZONTAL));
				return;
			}
		}

		if (set.contains(new Structure(x + 1, y, HORIZONTAL))) {
			// 오른쪽 보와 연결되어있는 보에 기둥이 없는 경우
			set.remove(new Structure(x, y, HORIZONTAL));
			if (!addValid(x + 1, y, false, set)) {
				set.add(new Structure(x, y, HORIZONTAL));
				return;
			}
		}

		if (set.contains(new Structure(x - 1, y, HORIZONTAL))) {
			// 왼쪽 보와 연결되어있는 보에 기둥이 없는 경우
			set.remove(new Structure(x, y, HORIZONTAL));
			if (!addValid(x - 1, y, false, set)) {
				set.add(new Structure(x, y, HORIZONTAL));
				return;
			}
		}

		set.remove(new Structure(x, y, HORIZONTAL));
		return;
	}

	public boolean addValid(int x, int y, boolean isVertical, TreeSet<Structure> set) {
		if (isVertical) { // 기둥
			if (y == 0) {
				// 바닥 위에 있는 경우
				set.add(new Structure(x, y, VERTICAL));
				return true;
			}

			if (set.contains(new Structure(x - 1, y, HORIZONTAL)) || set.contains(new Structure(x, y, HORIZONTAL))) {
				// 보의 한쪽 끝 부분 위에 있는 경우
				set.add(new Structure(x, y, VERTICAL));
				return true;
			}

			if (set.contains(new Structure(x, y - 1, VERTICAL))) {
				// 다른 기둥 위에 있는 경우
				set.add(new Structure(x, y, VERTICAL));
				return true;
			}

			return false;
		}

		// 보
		if (set.contains(new Structure(x + 1, y, HORIZONTAL)) && set.contains(new Structure(x - 1, y, HORIZONTAL))) {
			// 양쪽 끝 부분이 다른 보와 동시에 연결
			set.add(new Structure(x, y, HORIZONTAL));
			return true;
		}
		if (set.contains(new Structure(x, y - 1, VERTICAL)) || set.contains(new Structure(x + 1, y - 1, VERTICAL))) {
			// 한쪽 끝 부분이 기둥 위에 있는 경우
			set.add(new Structure(x, y, HORIZONTAL));
			return true;
		}

		return false;
	}
}

class Structure implements Comparable<Structure> {
	int x, y;
	int structure;

	public Structure(int x, int y, int structure) {
		this.x = x;
		this.y = y;
		this.structure = structure;
	}

	@Override
	public int compareTo(Structure o) {
		if (x == o.x) {
			if (y == o.y) {
				return Integer.compare(structure, o.structure);
			}
			return Integer.compare(y, o.y);
		}
		return Integer.compare(x, o.x);
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof Structure)) {
			return false;
		}
		Structure s = (Structure) o;
		return x == s.x && y == s.y && structure == s.structure;
	}

	@Override
	public int hashCode() {
		return Objects.hash(x, y, structure);
	}

	@Override
	public String toString() {
		if (structure == 1) {
			return "(" + x + ", " + y + ") 보";
		}
		return "(" + x + ", " + y + ") 기둥";
	}
}

문제 조건

  • 1단계에서는 음높이가 세 배가 되며, 2단계와 3단계에서 음높이가 각각 1씩 증가할 경우 3단고음 가능, 즉 *++ 형태만 가능하다.
    • 즉, 문자열은 항상 ++로 끝난다
  • 3단 점프를 한 후에는 음계를 한 단계씩 두 번을 더 높게 내야한다. 하지만 굳이 연속으로 1단 점프를 할 필요는 없다.
    • 예) *+*+++

 

 

n= 41일 때 answer=2이다. 예시를 보자

41 = (~~~)++

39 = (~~~)

 

39를 만드는 방법은 아래와 같다.

38+

37++

36+++

12*+++, 35+++

11+*++, 34++++, (4**+++의 경우는 불가)

...

class Solution {
	public int solution(int n) {
		return check(n - 2, 2);
	}

	private int check(int n, int p) { // n이 현재 값 , p가 plus의 개수
		if (n == 3 && p == 2) {
			return 1;
		}
		if (n < 3 || Math.log(n) / Math.log(3) * 2 < p) {
			return 0;
		}
		if (n == 3 && p == 3) {
			return 0;
		}

		return check(n - 1, p + 1) + (n % 3 == 0 && p > 1 ? check(n / 3, p - 2) : 0);
	}
}

문제 조건

회전판에 먹어야 할 1~N의 음식이 있다. 각 음식을 섭취하는데 일정 시간이 소요된다.

  • 무지는 1번 음식부터 먹기 시작하며, 회전판은 번호가 증가하는 순서대로 음식을 무지 앞으로 가져다 놓는다.
  • 마지막 번호의 음식을 섭취한 후에는 회전판에 의해 다시 1번 음식이 무지 앞으로 온다. => 원형 큐
  • 무지는 음식 하나를 1초 동안 섭취한 후 남은 음식은 그대로 두고, 다음 음식을 섭취한다.

무지가 먹방을 시작한 지 K 초 후에 방송이 잠시 중단되었는데, 다시 방송을 이어갈 때 섭취할 음식의 번호를 구하는 문제이다.

 

먼저 k가 음식의 갯수보다 큰지 작은지 생각해 보아야 한다.

  • k < food_times.length: (k+1) 번째 음식을 무조건 리턴
  • k > food_times.length: 맨 처음 들어있는 음식의 시간만큼 사이클을 돌 수 있는 경우
    • 한 cycle을 돌 수 있는 경우: k 시간에서 사이클을 도는 시간만큼 바로 빼준다
    • 한 cycle 전에 끝나는 경우: 큐에 있는 index를 크기순으로 정렬했을 때 몇번째 위치를 리턴해야 하는지 생각해본다

 

음.. 글로 설명하니까 좀 어려워 보이는데,,,,

프로그래머스 질문하기에 나와있는 예시를 한 번 시뮬레이션 해 보면

food_times=[4,2,3,6,7,1,5,8] k=27 => answer = 5

 

food = [(0, 4), (1, 2), (2, 3), (3, 6), (4, 7), (5, 1), (6, 5), (7, 8)] 이고 가장 작은 값을 가진 1만큼의 사이클을 돈다고 하면 1*8=8 => k-8=19

food = [(0, 4), (1, 2), (2, 3), (3, 6), (4, 7), (6, 5), (7, 8)] 그 다음 가장 작은 2-1 = 1만큼의 사이클을 돌면 1*7=7 => 19-7=12

food = [(0, 4), (2, 3), (3, 6), (4, 7), (6, 5), (7, 8)] 그 다음 가장 작은 3-2 = 1만큼의 사이클을 돌면 1*6=6 => 12-6=6

food = [(0, 4), (3, 6), (4, 7), (6, 5), (7, 8)] 그 다음 가장 작은 4-3 = 1만큼의 사이클을 돌면 1*5=5 => 6-5=1

food = [(3, 6), (4, 7), (6, 5), (7, 8)] 마지막으로 사이클을 온전히 다 돌지 못하기 때문에 1+1번째 요소를 찾으면 (4,7)이고 해당 answer=5이다.

 

즉, 한 개의 사이클을 온전히 돌 수 있다면 그 요소는 결국 리스트에서 사라지기 때문에 위치가 영향을 주지 않고,

food_times가 가장 작은 순으로 사이클을 돌아야 하기 때문에 자료구조는 priority queue가 적합하다.

 

한 개의 사이클을 온전히 돌 수 없다면 맨 처음 들어왔던 food_times의 인덱스 순으로 순회한다.

 

아래는 그 풀이이다.

import java.util.*;
import java.util.stream.Collectors;
class Solution {
	public int solution(int[] food_times, long k) {
		int length = food_times.length;

		if (k < length) {
			return (int) (k % length) + 1;
		}

		PriorityQueue<Food> queue = new PriorityQueue<Food>();
		for (int i = 0; i < food_times.length; i++) {
			queue.add(new Food(i, food_times[i]));
		}

		long minus = 0;
		while (!queue.isEmpty()) {
			Food now = queue.peek();
			long cycle = (long) (now.left - minus) * queue.size();
			if (k - cycle >= 0) { // 한바퀴
				k -= cycle;
				minus = now.left;
				queue.poll();

			} else { // 중간에 멈춤
				k %= queue.size();
				return queue.stream().mapToInt(food -> food.index).sorted().skip(k).findFirst().getAsInt() + 1;
			}
		}

		return -1;
	}
}

class Food implements Comparable<Food> {
	int index;
	int left;

	public Food(int index, int left) {
		this.index = index;
		this.left = left;
	}

	@Override
	public int compareTo(Food o) {
		return Integer.compare(left, o.left);
	}

	@Override
	public String toString() {
		return "(" + index + ", " + left + ")";
	}
}

  

 

문제 조건 

  1. 진열된 모든 종류의 보석을 적어도 1개 이상 포함하는 가장 짧은 연속된 구간을 찾아서 구매
  2. 가장 짧은 구간이 여러 개라면 시작 진열대 번호가 가장 작은 구간을 리턴

 

 

연속된 구간을 보는 것이기 때문에 투 포인터를 사용하여 풀면 된다고 생각했다.

진열대 위치를 쭉 훑으면서 맵에 저장되어있는 보석의 수가 1보다 크면 시작 위치를 하나씩 땡기는 방법으로 생각하고 문제를 풀었다. 

import java.util.*;
class Solution {
	public int[] solution(String[] gems) {
		int[] answer = new int[2];

		HashMap<String, Integer> selects = new HashMap<String, Integer>();
		HashSet<String> allGems = new HashSet<String>();

		int length = gems.length;
		answer[0] = 1;
		answer[1] = length;

		for (int i = 0; i < length; i++) {
			allGems.add(gems[i]);
		}
		int size = allGems.size();

		int start = 0;
		for (int end = 0; end < length; end++) {
			selects.put(gems[end], selects.getOrDefault(gems[end], 0) + 1);

			while (selects.getOrDefault(gems[start], 0) > 1) {
				selects.replace(gems[start], selects.get(gems[start]) - 1);
				start++;
			}

			if (selects.size() == size && answer[1] - answer[0] + 1 > end - start + 1) {
				answer[1] = end + 1;
				answer[0] = start + 1;
			}
		}

		return answer;
	}
}

+ Recent posts