다이나믹 프로그래밍
큰 문제를 작은 문제 여러개로 나눠서(주로크기를기준으로) 풀고 다시 원래의 큰 문제를 푸는 알고리즘
큰 문제를 작은 문제 여러개로 나눴을때, 작은 문제들이 중복이 가능하다.(분할정복은 중복허용X)
DP 문제에서 문제를 작게 만들때 한단계 그리고 나머지 단계로 나누는데, 한단계는 절대로 또 문제로 나누어지면 안된다.
한 단계에 들어갈 값을 정할수없다는것은 다해보면 된다
속성
두가지 속성을 만족해야 다이나믹 프로그래밍으로 문제를 풀 수 있다.
int memo[100];
int fibonacci(int n){
if(n<=1){
return n;
}
else{
if(memo[n]>0) return memo[n];
memo[n] = fibonacci(n-1) + fibonacci(n-2);
return memo[n];
}
}
기본문제
1로 만들기(https://www.acmicpc.net/problem/1463)
-> 문제를 작게 만들어야하기 때문에 DP를 생각해본다.
-> 점화식을 생각해보자
-> N을 1로 만드는 최소연산횟수 점화식
-> D[N] = N을 1로 만드는 최소연산횟수
D[N]= N->N/3(한 문제) + N/3-> 1(나머지문제)
D[N]= 1 + D[N/3]
D[N]= N->N/2(한 문제) + N/2-> 1(나머지문제)
D[N]= 1 + D[N/2]
D[N]= N-1(한 문제) + (N-1)-> 1(나머지문제)
D[N]= 1 + D[N-1]
-> D[N] = +1 min(D[N/3],D[N/2],D[N-1])
-> 점화식 시간 복잡도 = 함수의 호출횟수(=문제의 개수) X 함수의 시간복잡도(=문제 1개를 푸는데 필요한 시간복잡도) = O(N) * O(1) = O(N)
2xN 타일링(https://www.acmicpc.net/problem/11726)
-> 점화식을 생각해보자
-> D[N] = 2XN을 채우는 방법의 수
-> D[N] = D[N-1]+D[N-2]
2xN 타일링2(https://www.acmicpc.net/problem/11727)
-> 점화식을 생각해보자
-> D[N] = 2XN을 채우는 방법의 수
-> D[N] = D[N-1] + (2 X D[N-2])
1,2,3 더하기(https://www.acmicpc.net/problem/9095)
-> 점화식을 생각해보자
-> D[N] = n을 1,2,3의 합으로 나타내는 방법의 수
-> D[N] = D[N-1]+D[N-2]+D[N-3]
-> D[0] = 1 (1,2,3 아무것도 사용하지 않으므로)
카드구매하기(https://www.acmicpc.net/problem/11052)
-> 점화식을 생각해보자
-> D[N] = 카드 N개를 구매하는 비용의 최대값
-> 한 단계와 나머지 단계로 나눈다.
-> 한 단계에서 카드팩에 들어있는 카드의 개수는 알수없다? = DP에서는 알수없다는 것은 다해보면 된다고 생각하자
-> 한 단계에 들어갈수있는 카드팩 N개를 각각 집어 넣어본다.
-> D[N]= max(D(N-i)+ p[i]) (1<i<=n)
-> 시간복잡도= 전체문제의 개수(N) * 문제1개의 시간복잡도(N)
카드구매하기2(https://www.acmicpc.net/problem/16194)
-> 점화식을 생각해보자
-> D[N] = 카드 N개를 구매하는 비용의 최솟값
-> 한 단계와 나머지 단계로 나눈다.
-> 배열의 초기값을 잘 설정해야한다.
1,2,3 더하기 5(https://www.acmicpc.net/problem/15990)
-> 점화식을 생각해보자
-> D[i][j] = i 를 1,2,3의 합으로 나타내는 방법의수, 마지막에 사용한 수는 j
-> D[i][1]= i 를 1,2,3의 합으로 나타내는 방법의수, 마지막에 사용한 수는 1
- 바로 전에 사용할 수 있는 수는 2,3
- D[i-1][2]+ D[i-1][3]
-> D[i][2]= i 를 1,2,3의 합으로 나타내는 방법의수, 마지막에 사용한 수는 2
- 바로 전에 사용할 수 있는 수는 1,3
- D[i-2][1]+ D[i-2][3]
-> D[i][3]= i 를 1,2,3의 합으로 나타내는 방법의수, 마지막에 사용한 수는 3
- 바로 전에 사용할 수 있는 수는 1,2
- D[i-3][2]+ D[i-3][2]
-> 초기화도 신경써줘야한다.
-> 1,2,3 더하기처럼 D[0]=1 로 초기화하면 중복이 발생한다.
-> D[0][1]=1 ,D[0][2]=1 , D[0][3]=1로 초기화를 한다면
-> D[1][1]= D[0][2]+D[0][3] = 2 (중복이 발생하게 된다)
-> 따라서 이 문제는 예외처리를 해야한다.
-> D[i][1]=
-D[i-1][2]+D[i-1][3] (i>1)
-1(i==1)
-0(i<1)
-> D[i][2]=
-D[i-2][1]+D[i-2][3] (i>2)
-1(i==2)
-0(i<2)
-> D[i][3]=
-D[i-3][1]+D[i-3][2] (i>3)
-1(i==3)
-0(i<3)
-> 연속된 수를 처리할때는 마지막수를 체크해보자
쉬운 계단수(https://www.acmicpc.net/problem/10844)
-> 점화식을 생각해보자
-> D[N][L] = 길이가 N인 계단수,마지막수 L
-> D[N][L] = D[N-1][L-1]+D[N-1][L+1]
-> 예외처리: 마지막 수가 0이면 L-1 처리가 불가능, 마지막 수가 9이면 L+1=10 이므로 처리가 불가능
이친수(https://www.acmicpc.net/problem/2193)
-> 점화식을 생각해보자
-> D[N][L] = 길이가 N인 이친수의 개수, 마지막 수 L
-> D[N][1] = D[N-1][0], D[N][0] = D[N-1][0]+D[N-1][1]
-> 초기값: D[0][0]=0, D[0][1]=0, D[1][0] = 0 , D[1][1]= 1
-> 다른 방법으로
-> D[N]=N자리 이친수의 개수
-> D[N] = D[N-1]+D[N-2]
가장 긴 증가하는 부분수열(https://www.acmicpc.net/problem/11053)
-> longest increasing subsequence
-> D[i] = A[1], ... ,A[i]까지 수열이 있을때, A[i]를 마지막으로 하는 가장 긴 증가하는 부분 수열의 길이
-> d[i] = max(d[j])+1 (j<i,A[j]<A[i])
-> O(n^2)
가장 긴 증가하는 부분수열4(https://www.acmicpc.net/problem/14002)
-> 역추적 알고리즘
연속합(https://www.acmicpc.net/problem/1912)
-> d[i] = i번째 수로 끝나는 가장 큰 연속합
-> max(i번째수가 i-1번째와 연속, 연속X)
-> d[i] = max(d[i-1]+a[i], a[i])
-> O(N)
제곱수의합(https://www.acmicpc.net/problem/1699)
-> 최소개수를 구하는 문제
-> d[n] = 자연수 n을 표현하는데 필요한 항의 최소개수
-> d[n]= min(d[n-i^2]+1) (1<=i^2<=n)
-> 방법의 개수를 구한다고 하면
-> d[n] = ∑(d[n-i^2])
-> 시간복잡도 O(n * √n)
합분해(https://www.acmicpc.net/problem/2225)
-> 정수 k개를 더해서 경우의 수를 구하는 문제
-> d[k][n] = d[k-1][n-l] , l=마지막에 더하는 수 (l<=n)
-> d[k][n] =∑d[k-1][n-l] (0 <=l<=n)
-> O(KN^2)
연습문제
6. 포도주시식(https://www.acmicpc.net/problem/2156)
10. 가장 큰 바이토닉 부분수열(https://www.acmicpc.net/problem/11054)
-> 길이 최댓값을 구하는 문제
-> LIS와 LDS를 활용해서 문제를 푼다.
-> d[i] = i에서 끝나는 증가수열
-> d2[i] = i부터 시작하는 감소수열
-> d[k] = d[k]+d2[k]-1
11. 연속합2(https://www.acmicpc.net/problem/13398)
-> 최댓값 구하는 문제(수는 하나 제거할수있다)
-> d[i] = i에서 끝나는 합의 최댓값
-> d2[i] = i부터 끝나는 합의 최댓값
-> d[k-1]+d2[k+1]
도전문제
동물원(https://www.acmicpc.net/problem/1309)
-> 다른방식으로 푸는문제
-> 가로기준: 한마리도 배치x, 한마리 왼쪽칸에 배치, 한마리 오른쪽칸에 배치
-> d[i] = 세로크기가 i인 동물원을 채우는 방법의 수, 단 i번째에는 동물이 있어야한다.
-> i번째 줄의 이전에 동물이 있는 줄은 어디일까?
-> d[i]= d[i-1] + 2×d[i-2] + 2×d[i-2] + ... 2× d[1]
-> s[i] = d[1]+ ... d[i]
-> d[i] = d[i-1] + 2* si-2
-> S[N]이 정답이다
2. RGB거리2(https://www.acmicpc.net/problem/17404)
-> 최소비용구하는문제
-> d[i][j] = i번집을 색j로 칠했을때,1~i번 집을 칠하는 비용의 최솟값
+α