[문제 링크]

 

14501번: 퇴사

첫째 줄에 백준이가 얻을 수 있는 최대 이익을 출력한다.

www.acmicpc.net


지난번에 완전탐색으로 한번 풀었던 문제였는데 이번에 동적 계획법을 사용하여 다시 풀게되었다.

 

아래에서 위로 탐색하는 알고리즘은 금방 생각해냈지만, 위에서 아래로 탐색하여 정답을 얻어내는 알고리즘을 생각하다가 이틀이나 날려먹었다.....

 

일단은 아래에서 위로 재귀호출을 통해 탐색하는 방식으로 문제를 해결하였다.

이 문제의 핵심은 해당 일에 상담을 했을 때 얻는 최대이익과 상담을 하지 않았을 때 얻는 최대이익 중 더 큰 값을 선택한다는 것에 있다.

 

따라서 점화식은 다음과 같다.

dp(N) = max(dp(N+1), dp(N+T[N]) + P[N]);

 

위 점화식을 알고리즘으로 구현하면 원하는 결과를 얻을 수 있다.


 

#include <iostream>
using namespace std;

const int INF = 987654321;
int N, T[16], P[16], memo[16];
inline int max(int a, int b) { return a > b ? a : b; }

int brute(int start, int pay)
{
	int ret = pay;
	for (int i = start; i < N; i++)
	{
		int next = i + T[i];
		if (next > N) continue;
		ret = max(ret, brute(next, pay + P[i]));
	}
	return ret;
}

int Topdown(int start)
{
	if (start >= N) return start == N ? 0 : -INF;

	int& ret = memo[start];
	if (ret != 0) return ret;

	return ret = max(Topdown(start + 1), Topdown(start + T[start]) + P[start]);
}

int main(void)
{
	cin >> N;

	for (int i = 0; i < N; i++)
		cin >> T[i] >> P[i];

	// brute-force
	cout << brute(0, 0) << endl;

	// top-down
	cout << Topdown(0) << endl;

	return 0;
}

알고리즘 200일 프로젝트 - 68 day

[문제 링크]

 

9461번: 파도반 수열

문제 오른쪽 그림과 같이 삼각형이 나선 모양으로 놓여져 있다. 첫 삼각형은 정삼각형으로 변의 길이는 1이다. 그 다음에는 다음과 같은 과정으로 정삼각형을 계속 추가한다. 나선에서 가장 긴 �

www.acmicpc.net


간단한 dp 문제였다.

파도반 수열에는 일정한 규칙이 있는데, 이를 점화식으로 표현하면 다음과 같다.

 

padovan(N) = padovan(N-2) + padovan(N-3)

 

위 점화식을 구현하면 원하는 결과를 얻을 수 있다.


#include <iostream>
using namespace std;

long long memo[101];

long long Topdown(int N)
{
	if (N == 1) return 1;
	if (N == 2) return 1;
	if (N == 3) return 1;

	long long& ret = memo[N];
	if (ret != 0) return ret;

	return ret = Topdown(N - 2) + Topdown(N - 3);
}

long long Bottomup(int N)
{
	memo[1] = memo[2] = memo[3] = 1;

	for (int i = 4; i <= N; i++)
		memo[i] = memo[i - 2] + memo[i - 3];
	
	return memo[N];
}

int main(void)
{
	int tc;
	cin >> tc;
	while (tc--)
	{
		int N;
		cin >> N;

		// Top-down
		cout << "Top-down: " << Topdown(N) << endl;

		// Bottom-up
		cout << "Bottom-up: "<< Bottomup(N) << endl;
	}

	return 0;
}

알고리즘 200일 프로젝트 - 66 day

[문제 링크]

 

11727번: 2×n 타일링 2

2×n 직사각형을 1×2, 2×1과 2×2 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오. 아래 그림은 2×17 직사각형을 채운 한가지 예이다.

www.acmicpc.net


1x2, 2x1 타일을 사용해서 2 x n 직사각형을 채우는 문제의 업그레이드 버젼이다.

 

먼저, 1x2, 2x1 타일을 사용하는 타일링 문제의 점화식이 dp(n) = dp(n-1) + dp(n-2) 라는 것을 기억하자.

 

위 점화식에서 dp(n-1) 2x(n-1) 타일을 채우는 방법의 수 * 2x1 타일을 채우는 방법의 수를 나타내는데,

2 x 1 타일을 채우는 방법이 한가지밖에 존재하지 않기 때문에 *1 이 생략된 것이다.

 

dp(n-2) 도 마찬가지로 2x(n-2) 타일을 채우는 방법의 수 * 2x2 타일을 채우는 방법의 수를 나타내는데,

2x2 타일을 채우는 방법은 1x2 타일 2개로 채우는 방법과 2x1 타일 2개로 채우는 방법이 존재하지만

2x1 타일 2개로 채우는 방법은 dp(n-1)과 중복되므로 제외시킨다.

따라서 2x2 타일을 채우는 방법도 한가지 밖에 존재하지 않기 때문에 *1이 생략된 것이다.

 

이 문제는 1x2, 2x1 타일과 추가로 2x2 타일을 사용할 수 있다고 한다.

2x2 타일이 추가되었기 때문에 2x1 타일을 채우는 방법의 수는 변함이 없다.

하지만 2x2 타일을 채우는 방법은 1x2 타일 2개로 채우는 방법에 추가로 2x2 타일 1개로 채우는 방법이 생기게 되므로 총 두가지 방법이 존재하게 되어 * 2를 해주어야 한다.

 

따라서 이 문제의 점화식은 다음과 같다.

dp(n) = dp(n-1) + (dp(n-2) * 2)

 

위 점화식을 구현하면 원하는 결과를 얻을 수 있다.


#include <iostream>
using namespace std;

const int MOD = 10007;
int memo[1001];

int Topdown(int n)
{
	if (n == 1) return 1;
	if (n == 2) return 3;

	int& ret = memo[n];
	if (ret != 0) return ret;

	return ret = (Topdown(n - 1) + (Topdown(n - 2) * 2)) % MOD;
}

int Bottomup(int n)
{
	memo[1] = 1;
	memo[2] = 3;

	for (int i = 3; i <= n; i++)
		memo[i] = (memo[i - 1] + (memo[i - 2] * 2)) % MOD;

	return memo[n];
}

int main(void)
{
	int n;
	cin >> n;
	
	// Top-down
	cout << "Top-down: " << Topdown(n) << endl;
	
	// Bottom-up
	cout << "Bottom-up: "<< Bottomup(n) << endl;

	return 0;
}

알고리즘 200일 프로젝트 - 66 day

'알고리즘 > BOJ' 카테고리의 다른 글

백준 14501번: 퇴사 (dp)  (0) 2020.06.12
백준 9461번: 파도반 수열  (0) 2020.06.10
백준 11053번: 가장 긴 증가하는 부분 수열  (0) 2020.06.10
백준 10844번: 쉬운 계단 수  (0) 2020.06.10
백준 1912번: 연속합  (0) 2020.06.09

[문제 링크]

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이

www.acmicpc.net


알고스팟의 LIS 문제와 같은 문제였다. 

 

Top-down 방식의 알고리즘은 1~N번째에서 시작하여 아래 방향으로 1번째까지 탐색하면서 가장 긴 LIS를 찾아주도록 구현하였고, 기저사례로 현재 위치가 1번째일 경우 LIS는 반드시 1이므로 1을 반환하도록 하였다.

 

Bottom-up 방식의 알고리즘은 1번째에서 시작하여 윗 방향으로 1~N번째까지 탐색하면서 가장 긴 LIS를 찾아주도록 구현하였다. 1번째까지만 탐색하는 경우와 LIS가 최소일 때 길이는 무조건 1이므로 dp배열을 1로 초기화 해주었다.


#include <iostream>
using namespace std;

int arr[1001], memo[1001];

inline int max(int a, int b) { return a > b ? a : b; }

int TopDown(int here)
{
	if (here == 1) return 1;

	int& ret = memo[here];
	if (ret != 0) return ret;

	ret = 1;

	for (int i = here-1; i > 0; i--)
		if (arr[i] < arr[here])
			ret = max(ret, TopDown(i) + 1);

	return ret;
}

int BottomUp(int N)
{	
	for (int i = 0; i < 1001; i++)
		memo[i] = 1;

	int ret = 1;
	for (int i = 2; i <= N; i++)
	{
		for (int j = 1; j <= i; j++)
			if (arr[i] > arr[j] && memo[i] < memo[j] + 1)
				memo[i] = memo[j] + 1;

		ret = max(ret, memo[i]);
	}

	return ret;
}

int main(void)
{
	int N;
	cin >> N;
	for (int i = 1; i <= N; i++)
		cin >> arr[i];
	
	// Top-down
	int res = 0;
	for (int i = 1; i <= N; i++)
		res = max(res, TopDown(i));
	cout << "Top-down: " << res << endl;

	// Bottom-up
	cout << "Bottom-up: " << BottomUp(N) << endl;

	return 0;
}

알고리즘 200일 프로젝트 - 65 day

'알고리즘 > BOJ' 카테고리의 다른 글

백준 9461번: 파도반 수열  (0) 2020.06.10
백준 11727번: 2 x n 타일링 2  (0) 2020.06.10
백준 10844번: 쉬운 계단 수  (0) 2020.06.10
백준 1912번: 연속합  (0) 2020.06.09
백준 2156번: 포도주 시식  (0) 2020.06.09

[문제 링크]

 

10844번: 쉬운 계단 수

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net


5개월 전에 책 없이 공부할 때 결국 못풀고 포기한 문제였다. 그 당시엔 정말 어렵게 느껴졌던 문제였는데 동적계획법을 제대로 이해하고 보니 풀이법을 금방 생각해낼 수 있었다.

 

 0~9 사이의 숫자 number로 시작하는 길이가 N인 계단수는 number-1로 시작하는 길이가 N-1인 계단수 + number+1로 시작하는 길이가 N-1인 계단수와 같다. 그리고 number가 0일 때 number-1인 -1과 9일 때 number+1인 10은 0~9 사이의 숫자가 아니므로 계산에서 제외시켜야 한다.

따라서 이 문제의 점화식을 다음과 같이 표현할 수 있다.

 

if (number == 0)  dp(N, number) = dp(N-1, number+1)

else if (number == 9)  dp(N, number) = dp(N-1, number-1)

else  dp(N, number) = dp(N-1, number-1) + dp(N-1, number+1)

 

위 점화식을 알고리즘으로 구현하면 원하는 결과를 얻을 수 있다.


#include <iostream>
using namespace std;

const int MOD = 1000000000;
int memo[101][10];

int TopDown(int n, int here)
{
	if (n == 1) return 1;

	int& ret = memo[n][here];
	if (ret != 0) return ret;

	if (here == 0)
		return ret = TopDown(n - 1, here + 1) % MOD;
	else if (here == 9)
		return ret = TopDown(n - 1, here - 1) % MOD;
	else
		return ret = (TopDown(n - 1, here - 1) + TopDown(n - 1, here + 1)) % MOD;
}

int BottomUp(int n)
{
	for (int i = 0; i <= 9; i++)
		memo[1][i] = 1;

	for (int i = 2; i <= n; i++)
	{
		for (int j = 0; j <= 9; j++)
		{
			if (j == 0)
				memo[i][j] = memo[i - 1][j + 1] % MOD;
			else if (j == 9)
				memo[i][j] = memo[i - 1][j - 1] % MOD;
			else
				memo[i][j] = (memo[i - 1][j - 1] + memo[i - 1][j + 1]) % MOD;
		}
	}
	int res = 0;
	for (int i = 1; i <= 9; i++)
	{
		res += memo[n][i];
		res %= MOD;	// memo[n][i]가 3,000,000,00 정도의 값이라고 생각했을 때
        			// 1~9까지 반복하여 더하다보면 10억을 넘어갈 수 있기 때문에 %=MOD 해줌
	}

	return res;
}
int main(void)
{
	int n;
	cin >> n;
	
	// Top-Down
	int res = 0;
	for (int i = 1; i <= 9; i++)
	{
		res += TopDown(n, i);
		res %= MOD; // 위와 동일
	}
	cout << res << endl;

	// Bottom-Up
	cout << BottomUp(n) << endl;

	return 0;
}

알고리즘 200일 프로젝트 - 65 day

'알고리즘 > BOJ' 카테고리의 다른 글

백준 11727번: 2 x n 타일링 2  (0) 2020.06.10
백준 11053번: 가장 긴 증가하는 부분 수열  (0) 2020.06.10
백준 1912번: 연속합  (0) 2020.06.09
백준 2156번: 포도주 시식  (0) 2020.06.09
백준 2193번: 이친수  (0) 2020.06.08

[문제 링크]

 

1912번: 연속합

첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

www.acmicpc.net


이 문제는 dp 함수를 수행하면서 만들어지는 연속합 중 최대값을 저장해줘야 한다.

 

TopDown 알고리즘은 다음과 같다.

시작 위치가 n일 때 최대 연속합은 시작 위치가 n-1일 때 최대 연속합에 n번째가 가지는 정수를 더한 값과 n번째가 가지는 정수 중 더 큰 값이 최대 연속합이 된다. 따라서 점화식은 다음과 같이 표현할 수 있다. 

 

TopDown(n) = max(TopDown(n-1) + arr[n], arr[n])

 

위 점화식을 이용하여 첫번째부터 마지막까지를 시작 위치로 했을 때 나오는 최대 연속합을 구하면서 그중 최대값을 저장하고 출력하도록 구현하면 된다.

 

BottomUp 알고리즘도 탐색 순서만 다를뿐 TopDown 알고리즘과 동일하다.


#include <iostream>
using namespace std;

const int MINF = -987654321;

int arr[100001], memo[100001];

inline int max(int a, int b) { return a > b ? a : b; }

int TopDown(int here)
{
	if (here == 0) return arr[here];

	int& ret = memo[here];
	if (ret != MINF) return ret;

	ret = max(TopDown(here-1) + arr[here], arr[here]);

	return ret;
}

int BottomUp(int n)
{
	int ret;
	ret = memo[0] = arr[0];

	for (int i = 1; i < n; i++)
	{
		memo[i] = max(memo[i - 1] + arr[i], arr[i]);
		ret = max(ret, memo[i]);
	}

	return ret;
}

int main(void)
{
	for (int i = 0; i < 100001; i++)
		memo[i] = MINF;
	
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> arr[i];
	
	//Top-Down
	int res = MINF;
	for (int i = 0; i < n; i++)
		res = max(res, TopDown(i));
	cout << "TopDown: " << res << endl;
	
	//Bottom-Up
	cout << "BottomUp: " << BottomUp(n) << endl;

	return 0;
}

알고리즘 200일 프로젝트 - 64 day

'알고리즘 > BOJ' 카테고리의 다른 글

백준 11053번: 가장 긴 증가하는 부분 수열  (0) 2020.06.10
백준 10844번: 쉬운 계단 수  (0) 2020.06.10
백준 2156번: 포도주 시식  (0) 2020.06.09
백준 2193번: 이친수  (0) 2020.06.08
백준 1932번: 정수 삼각형  (0) 2020.06.08

[문제 링크]

 

2156번: 포도주 시식

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규

www.acmicpc.net


https://onlytrying.tistory.com/74 

계단 오르기와 유사한 형태의 문제였다.

 

차이점은 계단 오르기 문제는 마지막 계단을 반드시 밟아야 한다는 조건이 있었지만,

이 문제는 마지막 포도주를 반드시 마셔야 한다는 조건이 없다는 것이다.

따라서 마지막 포도주를 반드시 마시는 경우와 마시지 않는 경우의 최대값을 추가로 비교해줘야 원하는 결과를 얻을 수 있다.


#include <iostream>
using namespace std;

int wine[10001], memo[10001][2];

inline int max(int a, int b) { return a > b ? a : b; }

int TopDown(int here, int check)
{
	// check == 0 : 뒤에 포도주 마셨을 때
	// check == 1 : 뒤에 포도주 안 마셨을 때

	if (here < 0) return 0;

	int& ret = memo[here][check];
	if (ret != 0) return ret;

	if (check)
		ret = TopDown(here - 1, 0) + wine[here];

	return ret = max(ret, max(TopDown(here - 1, 1), TopDown(here - 2, 1) + wine[here]));
}

int BottomUp(int n)
{
	// memo[i][0] : 앞에 포도주 마셨을 때
	// memo[i][1] : 앞에 포도주 안 마셨을 때

	memo[0][0] = memo[0][1] = wine[0];
	memo[1][0] = wine[0] + wine[1];
	memo[1][1] = wine[1];

	for (int i = 2; i < n; i++)
	{
		memo[i][0] = max(memo[i - 1][1] + wine[i], memo[i - 1][0]);
		memo[i][1] = max(memo[i - 2][0] + wine[i], memo[i - 2][1] + wine[i]);
	}

	return max(memo[n - 1][0], memo[n - 1][1]);
}

int main(void)
{
	int n;
	cin >> n;

	for (int i = 0; i < n; i++)
		cin >> wine[i];

	//Top-Down
	cout << "TopDown: " << TopDown(n-1,1) << endl;
    
	//Bottom-Up
	cout << "BottomUp: "<< BottomUp(n) << endl;

	return 0;
}

 

 

알고리즘 200일 프로젝트 - 64 day

'알고리즘 > BOJ' 카테고리의 다른 글

백준 10844번: 쉬운 계단 수  (0) 2020.06.10
백준 1912번: 연속합  (0) 2020.06.09
백준 2193번: 이친수  (0) 2020.06.08
백준 1932번: 정수 삼각형  (0) 2020.06.08
백준 2579번: 계단 오르기  (0) 2020.06.08

[문제 링크]

 

2193번: 이친수

0과 1로만 이루어진 수를 이진수라 한다. 이러한 이진수 중 특별한 성질을 갖는 것들이 있는데, 이들을 이친수(pinary number)라 한다. 이친수는 다음의 성질을 만족한다. 이친수는 0으로 시작하지 않

www.acmicpc.net


간단한 DP문제였다. 이친수의 규칙을 살펴보면 N이 1씩 증가할때 이친수 갯수의 수열은 피보나치 수열을 이룬다.

따라서, 점화식은 DP(N) = DP(N-1) + DP(N-2) 이며 이를 구현하면 원하는 결과를 얻을 수 있다.


#include <iostream>
using namespace std;

long long memo[91];

long long TopDown(int N)
{
	if (N == 1 || N == 2) return 1;

	long long& ret = memo[N];
	if (ret != 0) return ret;

	return ret = TopDown(N - 1) + TopDown(N - 2);
}

long long BottomUp(int N)
{
	memo[1] = memo[2] = 1;

	for (int i = 3; i <= N; i++)
		memo[i] = memo[i - 1] + memo[i - 2];
	
	return memo[N];
}

int main(void)
{
	int N;
	cin >> N;

	// Top-Down
	cout << "TopDown: " << TopDown(N) << endl;
    
	// Bottom-Up
	cout << "BottomUp: "<< BottomUp(N) << endl;

	return 0;
}

알고리즘 200일 프로젝트 - 64 day

'알고리즘 > BOJ' 카테고리의 다른 글

백준 1912번: 연속합  (0) 2020.06.09
백준 2156번: 포도주 시식  (0) 2020.06.09
백준 1932번: 정수 삼각형  (0) 2020.06.08
백준 2579번: 계단 오르기  (0) 2020.06.08
백준 1149번: RGB거리  (0) 2020.06.08

+ Recent posts