주어진 예시를 보면 줄 세우기 과정은 아래와 같다.

3 7 5 2 6 1 4

3 7 4 5 2 6 1

3 4 5 2 6 1 7

1 3 4 5 2 6 7

 

즉, 제자리에 있는 아이들의 번호는 아래와 같다. == LIS

3 7 5 2 6 1 4

 

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);

		int n = in.nextInt();
		int child[] = new int[n + 1];
		int ans[] = new int[n + 1];
		for (int i = 1; i <= n; i++) {
			child[i] = in.nextInt();
		}
		ans[1] = 1;

		for (int i = 2; i <= n; i++) {
			int max = 0;
			for (int j = 0; j < i; j++) {
				if (child[i] > child[j]) {
					max = Math.max(max, ans[j]);
				}
			}
			ans[i] = max + 1;
		}

		Arrays.sort(ans);
		System.out.println(n - ans[n]);
	}
}

오랜만에 코드 잼을 풀어볼까 해서 운동 갔다온 후 켰음. 역시 영어라서 문제푸는 시간보다 해석하는데 더 오래걸림ㅠ 영어공부 좀 해야겟다

코드잼 Qualification 라운드는 30점만 넘기면 다음 라운드에 참가할 수 있다.

 

Reversort

주어진 reversort 슈도 코드를 시키는대로 구현하면 되는 문제였다

import java.util.*;

public class Solution {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int testcase = in.nextInt();
		for (int t = 1; t <= testcase; t++) {
			int n = in.nextInt();
			int[] arr = new int[n];
			for (int i = 0; i < n; i++) {
				arr[i] = in.nextInt();
			}

			int answer = 0;
			for (int i = 0; i < arr.length - 1; i++) {
				int j = minIndex(i, arr);
				reverse(i, j, arr);

				answer += (j - i + 1);
			}

			System.out.println("Case #" + t + ": " + answer);
		}
	}

	private static void reverse(int start, int end, int[] arr) {
		for (int i = 0; i <= (end - start) / 2; i++) {
			int temp = arr[start + i];
			arr[start + i] = arr[end - i];
			arr[end - i] = temp;
		}
	}

	private static int minIndex(int start, int[] arr) {
		int index = start;
		int value = arr[start];
		for (int i = start; i < arr.length; i++) {
			if (value > arr[i]) {
				value = arr[i];
				index = i;
			}
		}
		return index;
	}
}

 

Moons and Umbrellas

CJ가 나타나면 값x, JC가 나타나면 값y를 지불할 때 가장 작은 값을 리턴하는 문제이다.

일단은.. 테스트셋 2까지만 맞추는 것을 목표로 문제를 풀었다.

변수 범위는 아래와 같다.

1≤S.length()≤1000
1≤X≤100
1≤Y≤100

?가 있을 수 있는 값은 1000가지이고 모든 경우에 대해 구할 때 2^1000이 나와버리기 때문에 DP를 사용해야겠다고 생각했다.

dp[i][0]: 인덱스 i 가 ?일때, C를 넣을때 최솟값

dp[i][1]: 인덱스 i 가 ?일때, J를 넣을 때 최솟값으로 두고 아래와 같은 방식으로 풀어보기로 했다.

 

 

if-else, case문이 넘 드러워서 좀 간단히 할 수 있을 것 같긴 하지만... 일단 제출한 풀이는 아래와 같다.

import java.util.*;

public class Solution {

	private static final int C = 0;
	private static final int J = 1;

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int testcase = in.nextInt();
		for (int t = 1; t <= testcase; t++) {
			int x = in.nextInt(); // CJ
			int y = in.nextInt(); // JC
			char[] input = in.next().toCharArray();
			int length = input.length;

			int dp[][] = new int[length][2];
			for (int i = 1; i < length; i++) {
				if (input[i] == '?') {
					switch (input[i - 1]) {
					case '?':
						dp[i][C] = Math.min(dp[i - 1][C], dp[i - 1][J] + y);
						dp[i][J] = Math.min(dp[i - 1][J], dp[i - 1][C] + x);
						break;
					case 'C':
						dp[i][C] = dp[i - 1][C];
						dp[i][J] = dp[i - 1][C] + x;
						break;
					case 'J':
						dp[i][C] = dp[i - 1][J] + y;
						dp[i][J] = dp[i - 1][C];
						break;
					}
				} else if (input[i] == 'C') {
					switch (input[i - 1]) {
					case '?':
						dp[i][C] = Math.min(dp[i - 1][C], dp[i - 1][J] + y);
						dp[i][J] = Math.min(dp[i - 1][C], dp[i - 1][J] + y);
						break;
					case 'C':
						dp[i][C] = dp[i - 1][C];
						dp[i][J] = dp[i - 1][J];
						break;
					case 'J':
						dp[i][C] = dp[i - 1][C] + y;
						dp[i][J] = dp[i - 1][J] + y;
						break;
					}
				} else { // input[i] == 'J'
					switch (input[i - 1]) {
					case '?':
						dp[i][C] = Math.min(dp[i - 1][C] + x, dp[i - 1][J]);
						dp[i][J] = Math.min(dp[i - 1][C] + x, dp[i - 1][J]);
						break;
					case 'C':
						dp[i][C] = dp[i - 1][C] + x;
						dp[i][J] = dp[i - 1][J] + x;
						break;
					case 'J':
						dp[i][C] = dp[i - 1][C];
						dp[i][J] = dp[i - 1][J];
						break;
					}
				}
			}
			System.out.println("Case #" + t + ": " + Math.min(dp[length - 1][0], dp[length - 1][1]));
		}
	}
}

 

Reversort Engineering

요번에는 1번 Reversort와 달리 cost가 주어지면 리스트를 찾는 문제이다. 불가능하면 IMPOSSIBLE을 리턴하도록 한다.

일단 테스트셋 1의 경우 2N7이므로 permutation을 모두 구한 뒤 cost를 계산하고, 알맞은지 알아보도록 했다.

(이렇게 풀면 당연히 테스트셋2는 2N≤100이기 때문에 TLE가 나온다... 어떻게 해야하는지 모르겟음.. cost를 적당히 합으로 나눠서 리스트를 만드는건가.. 나중에 다른 사람들 풀이 봐야겟다..)

import java.util.*;

public class Solution {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int testcase = in.nextInt();
		for (int t = 1; t <= testcase; t++) {
			int n = in.nextInt();
			int cost = in.nextInt();

			int[] arr = new int[n];
			for (int i = 0; i < n; i++) {
				arr[i] = i + 1;
			}

			boolean isPossible = calculateCost(arr) == cost;

			// create permutations
			while (!isPossible) {
				int idex = findIndex(arr, n);
				if (idex == -1) {
					break;
				}

				int jdex = 0;
				for (int j = n - 1; j >= idex; j--) {
					if (arr[j] > arr[idex - 1]) {
						jdex = j;
						break;
					}
				}

				int temp = arr[jdex];
				arr[jdex] = arr[idex - 1];
				arr[idex - 1] = temp;

				int brr[] = new int[n];
				int bindex = 0;
				for (int i = 0; i < idex; i++) {
					brr[bindex++] = arr[i];
				}
				for (int i = n - 1; i >= idex; i--) {
					brr[bindex++] = arr[i];
				}
				if (calculateCost(brr) == cost) {
					isPossible = true;
					arr = brr;
					break;
				}
				arr = brr;
			}
			System.out.print("Case #" + t + ": ");
			printAnswer(arr, isPossible);
		}
	}

	private static int findIndex(int arr[], int n) {
		for (int i = n - 1; i >= 1; i--) {
			if (arr[i - 1] < arr[i])
				return i;
		}
		return -1;
	}

	private static int[] clone(int[] list) {
		int[] newList = new int[list.length];
		for (int i = 0; i < list.length; i++) {
			newList[i] = list[i];
		}
		return newList;
	}

	private static int calculateCost(int[] arr) {
		int[] list = clone(arr);
		int answer = 0;
		for (int i = 0; i < list.length - 1; i++) {
			int j = minIndex(i, list);
			reverse(i, j, list);

			answer += (j - i + 1);
		}
		return answer;
	}

	private static void reverse(int start, int end, int[] arr) {
		for (int i = 0; i <= (end - start) / 2; i++) {
			int temp = arr[start + i];
			arr[start + i] = arr[end - i];
			arr[end - i] = temp;
		}
	}

	private static int minIndex(int start, int[] arr) {
		int index = start;
		int value = arr[start];
		for (int i = start; i < arr.length; i++) {
			if (value > arr[i]) {
				value = arr[i];
				index = i;
			}
		}
		return index;
	}

	private static void printAnswer(int[] answer, boolean isPossible) {
		if (!isPossible) {
			System.out.println("IMPOSSIBLE");
			return;
		}

		for (int a : answer) {
			System.out.print(a + " ");
		}
		System.out.println();
	}
}

 

 

여기까지.. 31점을 얻었다 일단은..

 

'알고리즘 공부' 카테고리의 다른 글

SWEA 1248: 공통조상  (0) 2021.01.17
SWEA 3462: 선표의 축구 경기 예측  (0) 2021.01.10
SWEA 1949: 등산로 조성  (0) 2021.01.07
SWEA 1267: 작업순서  (0) 2021.01.03
SWEA 5653: 줄기세포 배양  (0) 2021.01.03

행렬 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;
	}
}

 

 

nums 배열에 있는 값을 적절히 더하고 빼서 target과 같은 값을 만들 수 있는 방법의 수를 구하는 문제이다.

일단 문제를 풀 수 있는 방법으로 가장 먼저 떠오른 것은 브루트 포스,,, 시간복잡도는 O(2^N)이다.

class Solution {
	public int answer = 0;

	public int findTargetSumWays(int[] nums, int S) {
		findAll(nums, 0, 0, S);
		return answer;
	}

	public void findAll(int[] nums, int index, int sum, int target) {
		if (index >= nums.length) {
			if (sum == target) {
				answer++;
			}
			return;
		}

		findAll(nums, index + 1, sum - nums[index], target);
		findAll(nums, index + 1, sum + nums[index], target);
	}
}

 

이 방법 외에 다른 방법이 있지 않을까 생각해 봤다.

  • The length of the given array is positive and will not exceed 20.
  • The sum of elements in the given array will not exceed 1000.
  • Your output answer is guaranteed to be fitted in a 32-bit integer.

문제 조건을 다시 보면 num의 합은 1000을 넘지 않는다고 했다.

합이 있으면 차도 가능하니 num을 적절히 더하고 빼면 그 범위는 -1000~1000일것이다.

이를 dp를 이용해서 구하도록 하는데, 음수 인덱스는 불가능 하니

dp[i][sum+1000]: num의 i번째 요소까지 더하고 뺐을 때, sum을 만들 수 있는 가짓수로 정의하도록 한다. 이 경우 시간복잡도는 O(N)이다.

import java.util.*;
class Solution {
	public int findTargetSumWays(int[] nums, int S) {
		int[][] memo = new int[nums.length][2001];
		for (int[] row : memo) {
			Arrays.fill(row, Integer.MIN_VALUE);
		}
		return calculate(nums, 0, 0, S, memo);
	}

	public int calculate(int[] nums, int i, int sum, int S, int[][] memo) {
		if (i == nums.length) {
			if (sum == S) {
				return 1;
			}
			return 0;
		}

		if (memo[i][sum + 1000] != Integer.MIN_VALUE) {
			return memo[i][sum + 1000];
		}

		int add = calculate(nums, i + 1, sum + nums[i], S, memo);
		int subtract = calculate(nums, i + 1, sum - nums[i], S, memo);
		memo[i][sum + 1000] = add + subtract;
		return memo[i][sum + 1000];
	}
}

이 문제에서 주의해야할 점은 아래 2가지이다.

  • EOF 처리
  • 최장부분증가수열을 빠르게 구하기
    • 완전탐색 => O(2^N)

 

1차 시도 - `동적 계획법`을 사용하여 문제를 풀어보았다. 결과는 50%에서 시간초과.

=> O(N^2), N ≤ 100000이므로 1초안에 통과될 수 없다. 아마 10초정도 걸릴것임...ㅠㅠ

 

dp[n]: n번째 수를 마지막으로 하는 최장증가부분수열로 정의했다.

import java.util.*;

public class Main {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		while (in.hasNextInt()) {

			int n = in.nextInt();
			int[] stock = new int[n];
			int[] dp = new int[n];
			int max = 0;
			for (int i = 0; i < n; i++) {
				stock[i] = in.nextInt();
			}

			dp[0] = 1;

			for (int i = 1; i < n; i++) {
				dp[i] = 1;
				for (int j = 0; j < i; j++) {
					if (stock[i] > stock[j]) {
						dp[i] = Math.max(dp[j], dp[j] + 1);
					}
				}
				max = Math.max(max, dp[i]);
			}
			System.out.println(max);
		}
	}
}

 

 

2차 시도 - N ≤ 100000이므로 1초안에 통과하려면 시간복잡도가 적어도 NlogN을 되어야 한다. => `이분탐색`을 이용한다

stock 수열을 처음부터 보면서

1. dp의 끝 값과 비교했을 때 마지막 값보다 큰 경우 dp의 마지막에 바로 삽입

2. dp의 끝 값과 비교했을 때 마지막 값보다 작거나 같은 경우 이분탐색을 이용하여 LIS를 유지하기 위한 위치에 수를 삽입하도록 한다.

https://www.acmicpc.net/problem/3745 예제

 

 

 

 

 

 

stock[0]을 dp 리스트에 넣는다.

 

 

stock[1]이 어디에 삽입될 수 있는지 인덱스를 이분탐색을 이용하여 구한다. (index=0)

 

 

stock[2]가 어디에 삽입될 수 있는지 인덱스를 이분탐색을 이용하여 구한다. (index=0)

 

 

stock[3]은 1보다 크므로 바로 삽입

 

 

stock[4]는 4보다 크므로 바로 삽입

 

 

 

stock[5]가 어디에 삽입될 수 있는지 인덱스를 이분탐색을 이용하여 구한다. (index=1)

 

 

 

 

 

 

예제에서 볼 수 있듯이 최종 dp 수열은 진짜 최장 증가 부분 수열을 이루는 요소와는 상관 없으며, lis의 길이만 연관이 있다는 점이 중요하다

위 알고리즘을 적용하여 코드를 짜 보면 아래와 같다.

import java.util.*;

public class Main {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		while (in.hasNextInt()) {

			int n = in.nextInt();
			int[] stock = new int[n];
			int[] dp = new int[n];
			for (int i = 0; i < n; i++) {
				stock[i] = in.nextInt();
			}

			dp[0] = stock[0];
			int lastIndex = 0;

			for (int i = 1; i < n; i++) {
				if (dp[lastIndex] < stock[i]) {
					lastIndex++;
					dp[lastIndex] = stock[i];
				} else {
					int index = findIndex(dp, stock[i], lastIndex);
					dp[index] = stock[i];
				}
			}
			System.out.println(lastIndex + 1);
		}
	}

	public static int findIndex(int[] dp, int num, int lastIndex) {
		int left = 0;
		int right = lastIndex;

		while (left < right) {
			int mid = (left + right) / 2;
			if (dp[mid] == num) {
				return mid;
			}

			if (dp[mid] > num) {
				right = mid;
			} else {
				left = mid + 1;
			}
		}

		return right;
	}
}

 

 

다른 방법으로는 `segment tree`를 사용하는 방법도 있다. segment tree를 사용하여 lis 길이를 구하는 방법은 아래와 같다.

1. 세그먼트 트리를 모두 0으로 초기화한다. , (stock[i], i) 객체를 만든 후 stock[i] 값에 대해 오름차순으로 정렬한다.

2. (stock[i], i)를 순회하면서 stock[i]에 대해 구간 [0, i]에서 최댓값을 구하고, 그 최대값+1을 세그먼트 트리의 인덱스 i인 리프 노드 값으로 업데이트한다. => i에 대해 구간 [0, i]에 지금까지 존재하는 LIS 길이+1 은 결국 stock[i]으로 끝나는 LIS길이와 같다.

3. 정렬된 객체를 순회하면서 LIS의 max 값을 찾는다.

 

 

 

가장 먼저 stock[i]=1인 i=2를 방문하게 된다.

먼저 구간 [0, 2]의 최댓값은 0이므로 1로 끝나는 LIS 길이는 1이고,

그 1 값을 인덱스 2에 업데이트 해 준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그 다음 stock[i]=2인 i=1를 방문하게 된다.

마찬가지로 [0, 1]의 최댓값은 0이므로 인덱스 1에 업데이트 해준다.

 

이때, 기존 LIS 결과가 증가하지는 않는다.

일단 이후 값이 언제 증가되는지 좀 더 보기로 하자.

 

 

 

 

 

 

 

 

 

 

 

 

그 다음 stock[i]=3인 i=5를 방문하게 되는데,

[0, 5]의 최댓값은 1이므로 인덱스 5에 2를 업데이트 해준다.

 

즉, 새로 보는 원소가 기존의 LIS 길이를 증가시키려면 

그 원소가 기존의 LIS보다 뒤에 나타나야 한다.

=> 이를 확인하는 것이 결국 구간 [0, i]의 최댓값을 보는 것이다.

이렇게 세그먼트 트리를 이용하여 stock[i]의 위치에 i번째 수를 마지막 원소로 가지는 lis의 길이를 업데이트함으로써 lis의 가장 긴 길이를 구할 수 있다.

 

 

 

 

 

 

 

 

마찬가지로 stock[i]=4인 i=3를 방문하게 된다.

[0, 3]의 최댓값은 1이므로 인덱스 3에 1+1=2를 업데이트 해준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

stock[i]=5인 i=0를 방문하게 된다.

[0, 0]의 최댓값은 0이므로 인덱스 0에 0+1=1을 업데이트 해준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

마지막으로 stock[i]=5인 i=4를 방문하게 된다.

[0, 4]의 최댓값은 2이므로 인덱스 4에 2+1=3을 업데이트 해준다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

최종 구현을 직접 코드로는 안해봤지만... 나중에 업데이트 해 볼 생각히다

 

'알고리즘 공부 > boj' 카테고리의 다른 글

boj 1949: 우수 마을  (0) 2021.01.19
boj 1981: 배열에서 이동  (0) 2021.01.17
boj 1091: 카드 섞기  (0) 2021.01.11
boj 1153: 네 개의 소수  (0) 2021.01.10
boj 17182: 우주 탐사선  (0) 2021.01.10

방법1 - 각 cell에서 1인 부분을 큐에 넣고 bfs를 통해 0까지의 최소 거리를 찾는다.

import java.util.*;
class Solution {
	public static int dr[] = { 0, 0, 1, -1 };
	public static int dc[] = { 1, -1, 0, 0 };

	public int[][] updateMatrix(int[][] matrix) {
		Queue<Position> queue = new LinkedList<Position>();
		for (int i = 0; i < matrix.length; i++) {
			for (int j = 0; j < matrix[i].length; j++) {
				if (matrix[i][j] != 0) {
					queue.add(new Position(i, j, 0));
				}
			}
		}

		while (!queue.isEmpty()) {
			Position now = queue.poll();
			updateCell(now.row, now.col, matrix);
		}
		return matrix;
	}

	public void updateCell(int row, int col, int[][] matrix) {
		Queue<Position> queue = new LinkedList<Position>();
		queue.add(new Position(row, col, 0));

		while (!queue.isEmpty()) {
			Position now = queue.poll();

			if (matrix[now.row][now.col] == 0) {
				matrix[row][col] = now.value;
				return;
			}

			for (int i = 0; i < 4; i++) {
				int newRow = now.row + dr[i];
				int newCol = now.col + dc[i];

				if (isBoundary(newRow, newCol, matrix)) {
					queue.add(new Position(newRow, newCol, now.value + 1));
				}
			}
		}
	}

	public boolean isBoundary(int row, int col, int[][] matrix) {
		if (row < 0 || row >= matrix.length) {
			return false;
		}
		if (col < 0 || col >= matrix[row].length) {
			return false;
		}
		return true;
	}

	public void printMatrix(int[][] matrix) {
		for (int i = 0; i < matrix.length; i++) {
			for (int j = 0; j < matrix[i].length; j++) {
				System.out.print(matrix[i][j] + " ");
			}
			System.out.println();
		}
	}
}

class Position {
	int row, col, value;

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

 

방법2 - 어차피 matrix에 거리를 계속 업데이트 하고 있는데 이 것을 이용해서 다음 값을 구할 수 없을까? => DP

class Solution {
	public int[][] updateMatrix(int[][] matrix) {
		int n = matrix.length;
		int m = matrix[0].length;
		int[][] dp = matrix;
		int INF = 10000;

		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (matrix[i][j] == 1) {
					dp[i][j] = INF;
				}
			}
		}

		for (int i = 1; i < n; i++) {
			dp[i][0] = Math.min(dp[i][0], dp[i - 1][0] + 1);
		}
		for (int i = 1; i < m; i++) {
			dp[0][i] = Math.min(dp[0][i], dp[0][i - 1] + 1);
		}

		for (int i = 1; i < n; i++) {
			for (int j = 1; j < m; j++) {
				dp[i][j] = Math.min(dp[i][j], Math.min(dp[i][j - 1], dp[i - 1][j]) + 1);
			}
		}

		for (int i = n - 2; i >= 0; i--) {
			dp[i][m - 1] = Math.min(dp[i + 1][m - 1] + 1, dp[i][m - 1]);
		}
		for (int i = m - 2; i >= 0; i--) {
			dp[n - 1][i] = Math.min(dp[n - 1][i + 1] + 1, dp[n - 1][i]);
		}

		for (int i = n - 2; i >= 0; i--) {
			for (int j = m - 2; j >= 0; j--) {
				dp[i][j] = Math.min(dp[i][j], Math.min(dp[i][j + 1], dp[i + 1][j]) + 1);
			}
		}

		return dp;
	}
}

'알고리즘 공부 > leetcode' 카테고리의 다른 글

leetcode: Maximum Frequency Stack  (0) 2021.01.23
leetcode: Range Sum Query - Mutable  (0) 2021.01.10
leetcode: Number of Islands  (0) 2021.01.07
leetcode: Trapping Rain Water  (0) 2020.12.27
leetcode: Median of two sorted Arrays  (0) 2020.12.26

+ Recent posts