공부/알고리즘 (c++)

동적 계획법

원클릭쓰리버그 2022. 3. 25. 13:41
728x90
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <windows.h>
using namespace std;

// 동적 계획법 (DP)

// memoization
int cache[50][50];

// n개에서 r개를 추출할 경우의 수를 계산하는 함수
int combination(int n, int r)
{
	// 0개를 뽑을 경우 및 n개에서 n개를 뽑을 경우 경우의 수는 1개이다. 
	if (r == 0 || n == r)
		return 1;

	// 이미 답을 구한 적 있으면 바로 반환
	int& ret = cache[n][r];
	if (ret != -1)
		return ret;

	// 새로 답을 구해서 캐시에 저장
	return ret = combination(n - 1, r - 1) + combination(n - 1, r);
}
// 재귀 함수를 하게되면 중복계산이 발생하므로 이를 우회하거나 막는다면, 효율적인 계산이 가능하다.

int main()
{
	::memset(cache, -1, sizeof(cache));

	__int32 start = GetTickCount64();

	int lotto = combination(45, 6);

	__int32 end = GetTickCount64();

	cout << lotto << endl;
	cout << end - start << " ms" << endl;
}

 

 

응용

 

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <windows.h>
using namespace std;

// 동적 계획법 (DP)
// LIS (Longest Increasing Sequence)

// Seq : 1 9 2 5 7
// 부분 수열 : 일부(0개 이상) 숫자를 지우고 남은 수열
// ex_ 1 2 5
// ex_ 1 9 5 7 
// 순 증가 부분 수열
// ex_ 1 2 5 (o)
// ex_ 1 9 5 7 (x)

// Q) LIS : 제일 긴 [순 증가 부분 수열]의 길이
// 정답_ 1 2 5 7  길이 4

vector<int> seq;
int cache[100];

int LIS(int s_idx)
{
	int& ret = cache[s_idx];
	if (ret != -1)
		return ret;

	// 최소 seq[pos]는 있으니 1부터 시작
	ret = 1;

	for (int next = s_idx+1; next<seq.size();next++)
	{
		if (seq[s_idx] < seq[next])
		{
			ret = max(ret, 1 + LIS(next));
		}
	}
	return ret;
}

int main()
{ 
	::memset(cache, -1, sizeof(cache));
	seq = vector<int>{1,9,2,5,7};
	int ret;
	for (int i = 0; i < seq.size(); i++)
	{
		ret = max(ret, LIS(i));
	}
}

 

 

응용 #2

 

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <windows.h>
using namespace std;

// 동적 계획법 (DP)
// TRIANGLE_PATH
//	(0,0)부터 시작해서 아래 OR 아래우축으로 이동 가능
//  만나는 숫자를 모두 더함
//  더한 숫자가 최대가 되는 경로의 합은?

//	6 
//	1 2
//	3 7 4
//	9 4 1 7 
//	2 7 5 9 4

int N;
vector<vector <int>> board;
vector<vector <int>> cache;
vector<vector <int>> nextX;

int path(int y, int x)
{
	//기저 사항
	if (y == N - 1)
		return board[y][x];	

	//캐시 확인
	int& ret = cache[y][x];
	if (ret != -1)
		return ret;
	// 경로 기록
	{
		int nextBottom = path(y + 1, x);
		int nextBottomRight = path(y + 1, x + 1);
		if (nextBottom > nextBottomRight)
			nextX[y][x] = x;
		else
			nextX[y][x] = x + 1;
	}

	ret = board[y][x] + max(path(y + 1, x), path(y + 1, x + 1));

	return ret;
}

int main()
{ 
	board = vector<vector<int>>
	{
		{6},
		{1,2},
		{3,7,4},
		{9,4,1,7},
		{2,7,5,9,4},
	};
	N = board.size();
	cache = vector<vector<int>>(N, vector<int>(N, -1));
	nextX = vector<vector<int>>(N, vector<int>(N));


	int ret = path(0, 0);
	cout << ret << endl;
	int x = 0;
	int y = 0;
	while (y<N)
	{
		cout << board[y][x] << "->";

		x = nextX[y][x];
		y++;
	}
}

'공부 > 알고리즘 (c++)' 카테고리의 다른 글

Prim 알고리즘  (0) 2022.03.24
Kruskal 알고리즘  (0) 2022.03.23
DisjointSet  (0) 2022.03.19
Hashmap (해쉬 맵)  (0) 2022.03.16
퀵 정렬 (Quick Sort)  (0) 2022.03.16