PS/Algorithm

2020/07/06 공부 - dynamic programming

hyelie 2022. 6. 21. 10:46

 backtrack을 끝내고 이제 DP를 해볼 차례다.

 DP는 원래 n square의 시간복잡도가 걸리는 일을 2가지 방법을 이용해 n에 linear하게 수행할 수 있도록 해주는 기법 중 하나이다. 이 2가지 방법은
 첫째, 현재의 state로 미래의 state를 계산해 주는 점화식
 둘째, 이미 계산했던 값이라면 계산했던 값을 사용하는 memoization
 두 가지를 사용한다.

 대표적으로 DP를 사용해서 시간을 줄일 수 있는 것이 조합이다. nCr에서 파스칼의 삼각형 특징을 이용해서 복잡한 계산을 조금 간단하게 줄일 수 있는데, 순열 점화식 

$_nC_0=_nC_n=1$
$_nC_r=_{n-1}C_{r-1}+_{n-1}C_r$
에서 2번째 점화식의 우항 부분이 계산되어 있다면 이를 저장해둔 후, 필요할 때 불러오는 방식을 사용한다.

 예제로 백준 2579번을 풀었다.

int dynamic_programming(vector<int>& point_per_stair, int N) {
	// memoization
	vector<int> dp(N);

	// 점화식의 초항
	dp[0] = point_per_stair[0];
	dp[1] = point_per_stair[0] + point_per_stair[1];
	dp[2] = point_per_stair[2] + max(point_per_stair[0], point_per_stair[1]);

	// 어떤 i번째 계단
	// i번째 계단을 계산하기 위해서는 i-1번째, i-2번째, i-3번째 계단까지 최댓값이 필요함.
	// 가능한 경우의 수; i, i-1, i-2는 존재하지 않음.
	// i번째 밟고 i-1번째 밟고 i-3번째 밟은 경우 (i-2번째 계단을 밟지 않아서)
	// i번째 밟고 i-2번째 밟은 경우

	for (int i = 3; i < N; i++) {
		dp[i] = max(dp[i - 3] + point_per_stair[i - 1] + point_per_stair[i], dp[i - 2] + point_per_stair[i]);
		// dp[i] = i번째 계단을 최종 계단으로 생각했을 때, 최대 점수
		// dp[i-3] = i-3번째 계단 밟았을 때 최고 점수
		// + point[i-1] + point[i] = i-3번째, i-1번째, i번째 계단 밟았을 때 점수
		// dp[i-2] = i-2번째 계단 밟았을 때 최고 점수
		// + point[i] = i-2번째 계단 밟고 i번째 계단 밟았을 때 점수
	}

	return dp[N-1];
}

 

 구현한 이유는 코드에 기술되어 있다. dp[i]는 i번째 계단을 최종 계단으로 생각했을 때, 그러니까 i번째 계단을 밟았을 때 최대 점수이다. 이 dp vector를 이용해서 점화식을 세울 수 있다. dp[i]의 특징을 이용하기 때문에 점화식이 한 줄로 나오게 된다.

 

void solve() {
	int N;
	cin >> N;
	vector<int> point_per_stair(N);
	for (int i = 0; i < N; i++) {
		cin >> point_per_stair[i];
	}

	cout << dynamic_programming(point_per_stair, N);
}