728x90

문제

 

 

접근 방식

수열 {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 이 주어졌을 때 1, 100, 2, 60, 50 처럼

들쑥날쑥한 부분 수열이 아닌 1, 2, 50, 60 처럼 증가하는 부분 수열 중에서

합이 가장 큰 부분 수열을 구해야 하는 문제다.

 

우선 간단하게 생각해보면 이전 수보다 큰 값이 나올 때까지 모두 합해주면

특정 수로 끝나는 부분 수열의 합을 구할 수는 있다.

1로 끝나는 부분 수열은 {1} = 1

1 > 100 > 2 (이전 수인 100보다 작음)
== 100으로 끝나는 부분 수열은 {1, 100} = 101

2 > 50 > 60 > 3 (이전 수인 60보다 작음)
== 60으로 끝나는 부분 수열은 {2, 50, 60} = 112

 

위와 같이 특정 수로 끝나는 부분 수열의 값을 알 수 있지만

문제에서 요구하는 부분 수열과는 조금 다르다.

 

문제에서는 {1, 2, 50, 60} = 113 처럼 연속된 부분 수열이 아니라도

값이 증가하기만 한다면 부분 수열로 만들 수 있기 때문에

{1, 2, 50, 60} 과 같은 수열을 만들기 위해서는

{1}과 {2, 50, 60}을 합치면 된다는 것을 알 수 있다.

 

즉, 60으로 끝나는 부분 수열의 합은

60보다 작은 부분 수열의 합들을 더한 값이 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int n = Integer.parseInt(br.readLine());

		int[] arr = new int[n];

		StringTokenizer st = new StringTokenizer(br.readLine());

		for (int i = 0; i < n; i++) {
			arr[i] = Integer.parseInt(st.nextToken());
		}

		int max = arr[0];
		int[] dp = new int[n];
		dp[0] = arr[0];

		for (int i = 1; i < n; i++) {
			dp[i] = arr[i];
			for (int j = 0; j < i; j++) {
				if (arr[j] < arr[i]) {
					dp[i] = Math.max(dp[j] + arr[i], dp[i]);
				}
			}

			max = Math.max(max, dp[i]);
		}

		System.out.println(max);
	}
}

 

'Java > Algorithms' 카테고리의 다른 글

[백준] 14501번 : 퇴사  (1) 2023.12.24
[백준] 9461번 : 파도반 수열  (1) 2023.12.23
[백준] 2193번 : 이친수  (0) 2023.12.20
[백준] 12852번 : 1로 만들기 2  (0) 2023.12.18
[백준] 11659번 : 구간 합 구하기 4  (0) 2023.12.18
728x90

문제

 

 

접근 방식

어떻게 풀어야할지 모르겠어서 일단 각 자리수별로 만들 수 있는

경우들을 직접 나열해보고서 규칙을 찾아보았다.

1 

10

100
101

1000
1001
1010

10000
10001
10010
10100
10101

100000
100001
100010
100100
100101
101000
101001
101010

1000000
1000001
1000010
1000100
1000101
1001000
1001001
1001010
1010000
1010001
1010010
1010100
1010101

 

각 자리수별로 경우의 수를 세어보면 1, 1, 2, 3, 5, 8, 13, ...

이런식으로 증가하는 것을 알 수 있는데 어디서 많이 본거 같다면

생각하는 그 피보나치 수열이 맞다.

 

1과 2의 경우만 초기값을 1로 지정하고

3자리부터 dp[i - 1] + dp[i - 2]를 계산해주면서

테이블을 채워주면 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int n = Integer.parseInt(br.readLine());

		long[] dp = new long[91];

		dp[1] = 1;
		dp[2] = 1;

		for (int i = 3; i <= n; i++) {
			dp[i] = dp[i - 2] + dp[i - 1];
		}

		System.out.println(dp[n]);
	}
}

 

728x90

문제

 

 

접근 방식

기존 1로 만들기 문제에 1이 되는 순서까지 출력하는게 추가된 문제기에

기존 DP 테이블 외에도 순서를 기록할 배열을 더 만들어서 기록해주면 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int n = Integer.parseInt(br.readLine());

		int[] dp = new int[n + 1];
		int[] history = new int[n + 1];

		for (int i = 2; i <= n; i++) {
			dp[i] = dp[i - 1] + 1;
			history[i] = i - 1;

			if (i % 2 == 0 && dp[i] > dp[i/2] + 1) {
				dp[i] = dp[i/2] + 1;
				history[i] = i/2;
			}

			if (i % 3 == 0 && dp[i] > dp[i/3] + 1) {
				dp[i] = dp[i/3] + 1;
				history[i] = i/3;
			}
		}

		StringBuilder sb = new StringBuilder();

		sb.append(dp[n]).append("\n");

		int num = n;

		while (true) {
			sb.append(num).append(" ");
			if (num == 1) break;
			num = history[num];
		}

		System.out.println(sb);
	}
}

 

728x90

문제

 

 

접근 방식

Prefix Sum 알고리즘을 사용해 풀 수 있는 문제로

i ~ j까지의 구간합은 (i 까지의 합) - (j-1까지의 합)이라는 공식을 이용해

DP 테이블에는 i까지의 합들을 저장하고 위의 공식대로 계산해주면 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int n = Integer.parseInt(st.nextToken());
		int m = Integer.parseInt(st.nextToken());

		int[] dp = new int[n + 1];

		st = new StringTokenizer(br.readLine());
		dp[1] = Integer.parseInt(st.nextToken());

		for (int i = 2; i <= n; i++) {
			dp[i] = dp[i - 1] + Integer.parseInt(st.nextToken());
		}

		StringBuilder sb = new StringBuilder();

		while (m-- > 0) {
			st = new StringTokenizer(br.readLine());

			int i = Integer.parseInt(st.nextToken());
			int j = Integer.parseInt(st.nextToken());

			sb.append(dp[j] - dp[i - 1]).append("\n");
		}

		System.out.println(sb);
	}
}

 

'Java > Algorithms' 카테고리의 다른 글

[백준] 2193번 : 이친수  (0) 2023.12.20
[백준] 12852번 : 1로 만들기 2  (0) 2023.12.18
[백준] 11727번 : 2×n 타일링 2  (1) 2023.12.18
[백준] 11726번 : 2×n 타일링  (1) 2023.12.18
[백준] 1149번 : RGB거리  (1) 2023.12.17
728x90

문제

 

 

접근 방식

11726번 문제에서 2*2 타일의 경우가 추가된 문제다.

 

i번째에 놓을 수 있는 경우가 기존 문제에서는 DP[i-1] + DP[i-2] 였다면

이번 문제에서는 DP[i-2]를 한 번 더 더해주면 된다.

 

DP[i-2]를 한 번 더 더해주는 이유는 이전 문제 설명글에서 설명한거처럼

1*2 타일을 놓는 경우는 나머지 한층도 1*2 타일을 놓을 수 밖에 없어

사실상 2*2타일을 놓는 경우와 같기 때문에

해당 경우를 한 번 더 계산해 주기만 하면 된다.

 

따라서 DP 테이블의 초기값은 기존 문제의 테이블에서

2번째 경우에 한 가지를 더 추가해서 DP[1] = 1, DP[2] = 3이 되고

이 테이블을 기준으로 계산해주면 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int n = Integer.parseInt(br.readLine());

		if (n == 1) {
			System.out.println(1);
			return;
		}

		int[] dp = new int[n + 1];

		dp[1] = 1;
		dp[2] = 3;

		for (int i = 3; i <= n; i++) {
			dp[i] = (dp[i-1] + dp[i-2] + dp[i-2]) % 10007;
		}

		System.out.println(dp[n]);
	}
}

 

728x90

문제

 

 

접근 방식

강의에서 설명해 주기 전까진 DP 문제일 줄은 생각도 못하고 있던 문제다...

 

 

일단 위와 같은 타일을 채워야 할 때

가장 위의 칸부터 채우는 경우를 살펴 보면

 

 

2*1 타일은 이렇게 하나를 둘 수 있는데

 

 

그러면 정확히 2*(n-1) 칸이 남게 된다.

 

 

1*2 타일의 경우에는 어차피 한 층에 1*2 타일을 쌓으면

다른 한 층도 무조건 1*2 타일을 쌓을 수밖에 없으니

2*(n-2) 칸이 남게 된다.

 

 

결국 이 한 칸에 올 수 있는 경우의 수는 (n-1) + (n-2)에 해당한다.

 

눈치가 빠른 사람은 이 공식을 어디서 많이 봤다고 생각할 수도 있는데

피보나치수열과 똑같은 공식이다.

 

이제 문제를 풀 수 있는 식은 구했으니

DP 테이블을 만들어야 하는데

간단하게 i일 때 경우의 수를 저장할 DP [N]으로 만들어주면 된다.

 

테이블의 초기값은 1일 때는 1, 2일 때는 2로 지정해 준 뒤에

3부터 반복문을 돌면서 DP [i-1] + DP [i-2]를 구한 후에

문제에서 요구한 대로 10007로 나눠 주면 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		int n = Integer.parseInt(br.readLine());

		if (n == 1) {
			System.out.println(1);
			return;
		}

		int[] dp = new int[n + 1];

		dp[1] = 1;
		dp[2] = 2;

		for (int i = 3; i <= n; i++) {
			dp[i] = (dp[i-1] + dp[i-2]) % 10007;
		}

		System.out.println(dp[n]);
	}
}

 

'Java > Algorithms' 카테고리의 다른 글

[백준] 11659번 : 구간 합 구하기 4  (0) 2023.12.18
[백준] 11727번 : 2×n 타일링 2  (1) 2023.12.18
[백준] 1149번 : RGB거리  (1) 2023.12.17
[백준] 20922번 : 겹치는 건 싫어  (1) 2023.12.17
[백준] 2531번 : 회전 초밥  (1) 2023.12.17
728x90

문제

 

 

접근 방식

백트래킹이나 다중반복문으로도 풀 수 있는 문제지만

시간제한을 보면 그렇게는 풀 수 없는 문제들이라 DP를 사용해서 풀었다.

 

1 ~ N번 집까지 있을 때, N번 집을 가장 적은 비용으로 칠할 수 있는 경우는

N-1번째 집을 칠한 경우 중 가장 적은 비용 + 현재 칠할 수 있는 색깔이다.

R	G	B
26	40	83
49	60	57
13	89	99

 

위와 같이 3개의 집과 각 집을 칠하는 색깔별 비용이 주어졌을 때를 살펴보면

26 -> 60 OR 57 -> 13 OR 99  / 13 OR 89
40 -> 49 OR 57 -> 89 OR 99  / 13 OR 89
83 -> 49 OR 60 -> 89 OR 99  / 13 OR 99

 

이전 집을 칠한 색깔을 제외한 각각의 색깔들 별로 칠할 수 있다.

 

DP 테이블을 각 집별로 3가지 색깔을 기록할 수 있게 만든 후에

이전 집의 최소 비용인 경우 + 현재 색깔을 칠하는 비용을 합해주면서

테이블을 모두 채워준 후에 마지막 집을 칠한 3가지 경우 중

가장 비용이 적은 경우를 출력해 주면 된다.

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;

		int n = Integer.parseInt(br.readLine());
		int[][] rgb = new int[n][3];

		for (int i = 0; i < n; i++) {
			st = new StringTokenizer(br.readLine());
			for (int j = 0; j < 3; j++) {
				rgb[i][j] = Integer.parseInt(st.nextToken());
			}
		}

		int[][] dp = new int[n][3];

		dp[0][0] = rgb[0][0];
		dp[0][1] = rgb[0][1];
		dp[0][2] = rgb[0][2];

		for (int i = 1; i < n; i++) {
			int r = rgb[i][0];
			int g = rgb[i][1];
			int b = rgb[i][2];

			int pr = dp[i - 1][0];
			int pg = dp[i - 1][1];
			int pb = dp[i - 1][2];

			dp[i][0] = Math.min(pg, pb) + r;
			dp[i][1] = Math.min(pr, pb) + g;
			dp[i][2] = Math.min(pr, pg) + b;
		}

		System.out.println(Math.min(dp[n - 1][0], Math.min(dp[n - 1][1], dp[n - 1][2])));
	}
}

 

728x90

문제

 

 

접근 방식

길이가 N인 수열에서 같은 숫자끼리는 최대 K번까지만 사용한 부분 수열을 구해야 한다.

 

각 숫자들마다 등장 횟수를 기록해야하기 때문에 불리언 배열이 아닌

정수형 배열에 특정 숫자가 등장 할 때마다 해당 인덱스에 카운트를 증가시키면서

투 포인터로 탐색을 하다가 특정 숫자가 K번 이미 등장 했을 때 또 등장했다면

만들 수 있는 부분 수열의 끝이므로 현재 부분 수열의 길이를 구한 후에

현재 까지 가장 긴 부분 수열의 길이인지 비교하고 값을 바꿔주면 된다. 

 

풀이

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int n = Integer.parseInt(st.nextToken());
		int k = Integer.parseInt(st.nextToken());

		int[] arr = new int[n];
		int[] useCount = new int[200_001];

		st = new StringTokenizer(br.readLine());

		for (int i = 0; i < n; i++) {
			arr[i] = Integer.parseInt(st.nextToken());
		}

		int end = 0;
		int max = 0;

		for (int start = 0; start < n; start++) {
			while (end < n) {
				if (useCount[arr[end]] == k) break;
				useCount[arr[end++]]++;
			}

			max = Math.max(max, end - start);

			useCount[arr[start]]--;
		}

		System.out.println(max);
	}
}

 

+ Recent posts