[문제 링크]

 

1309번: 동물원

첫째 줄에 우리의 크기 N(1≤N≤100,000)이 주어진다.

www.acmicpc.net


top-down으로 구현하다가 시간초과, 스택오버플로를 겪고 bottom-up으로 구현하였다.

 

이 문제를 N이 3일 때 까지 손으로 풀어보면 간단한 점화식을 발견할 수 있는데 점화식은 다음과 같다.

 

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

 

점화식은 간단하지만 필자는 위 점화식이 어떻게 참이 되는지 이해를 못하였기 때문에 위 점화식이 아닌 다른 방법으로 풀었다.

 

핵심은 옆에 칸에 사자를 배치했느냐 안했느냐에 따라서 경우의 수가 다르다는 것이다.


#include <iostream>
using namespace std;

const int MOD = 9901;
int memo[100001][2];

int Bottomup(int N)
{
	// [i][0] : 이전칸에 배치 했을 경우
	// [i][1] : 이전칸에 배치하지 않았을 경우
	memo[1][0] = 2;
	memo[1][1] = 3;

	for (int i = 2; i <= N; i++)
	{
		memo[i][0] = (memo[i - 1][0] + memo[i - 1][1]) % MOD;
		memo[i][1] = (memo[i - 1][0] * 2 + memo[i - 1][1]) % MOD;
	}

	return memo[N][1];
}

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

	cout << Bottomup(N) << endl;

	return 0;
}

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

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

백준 6359번: 만취한 상범  (0) 2020.07.06
백준 1937번: 욕심쟁이 판다  (0) 2020.07.06
백준 1890번: 점프  (0) 2020.07.03
백준 2225번: 합분해  (0) 2020.07.03
백준 1520번: 내리막 길  (0) 2020.07.03

[문제 링크]

 

1890번: 점프

문제 N×N 게임판에 수가 적혀져 있다. 이 게임의 목표는 가장 왼쪽 위 칸에서 가장 오른쪽 아래 칸으로 규칙에 맞게 점프를 해서 가는 것이다. 각 칸에 적혀있는 수는 현재 칸에서 갈 수 있는 거��

www.acmicpc.net


https://onlytrying.tistory.com/102

내리막길 문제와 비슷한 유형의 문제였다.

(y,x)에서 (N,N)으로 가는 경우의 개수는 항상 같기 때문에 이를 메모이제이션 하면 중복을 최소화할 수 있다.


#include <iostream>
#include <cstring>
using namespace std;

int N, arr[101][101];
long long memo[101][101];
int dy[2] = { 0, 1 }, dx[2] = { 1, 0 };

long long Topdown(int hereY, int hereX)
{
	if (hereY == N && hereX == N) return 1;
	if (arr[hereY][hereX] == 0) return 0;

	long long& ret = memo[hereY][hereX];
	if (ret != -1) return ret;

	ret = 0;

	int jump = arr[hereY][hereX];
	for (int i = 0; i < 2; i++)
	{
		int nextY = hereY + (dy[i] * jump);
		int nextX = hereX + (dx[i] * jump);

		if (nextY <= N && nextX <= N)
			ret += Topdown(nextY, nextX);
	}

	return ret;
}

long long Bottomup()
{
	memo[1][1] = 1;

	for(int i = 1; i<=N; i++)
		for (int j = 1; j <= N; j++)
		{
			if (arr[i][j] == 0)
				continue;

			if (i + arr[i][j] <= N)
				memo[i + arr[i][j]][j] += memo[i][j];

			if (j + arr[i][j] <= N)
				memo[i][j + arr[i][j]] += memo[i][j];
		}

	return memo[N][N];
}

int main(void)
{
	cin >> N;
	for (int i = 1; i <= N; i++)
		for (int j = 1; j <= N; j++)
			cin >> arr[i][j];

	memset(memo, -1, sizeof(memo));
	cout << Topdown(1, 1) << endl;

	memset(memo, 0, sizeof(memo));
	cout << Bottomup() << endl;

	return 0;
}

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

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

백준 1937번: 욕심쟁이 판다  (0) 2020.07.06
백준 1309번: 동물원  (0) 2020.07.05
백준 2225번: 합분해  (0) 2020.07.03
백준 1520번: 내리막 길  (0) 2020.07.03
백준 11054번: 가장 긴 바이토닉 부분 수열  (0) 2020.07.02

[문제 링크]

 

2225번: 합분해

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

www.acmicpc.net


간단한 dp 문제였다.

0부터 N까지의 정수 K개를 더해서 그 합이 N이 되는 경우의 수를 구하는 방법은 다음과 같다.

i를 0부터 N까지 반복하고 0부터 (N-i)까지의 정수 (K-1)개를 더해서 그 합이 (N-i)가 되는 경우의 수를 누적 시켜주면 된다.


#include <iostream>
#include <cstring>
using namespace std;

const int MOD = 1000000000;
int memo[201][201];

int Topdown(int N, int K)
{
	if (K == 0)
	{
		if (N == 0) return 1;
		else return 0;
	}

	int& ret = memo[N][K];
	if (ret != -1) return ret;

	ret = 0;

	for (int i = 0; i <= N; i++)
	{
		ret += Topdown(N - i, K - 1);
		ret %= MOD;
	}

	return ret;
}

int Bottomup(int N, int K)
{
	for (int i = 0; i <= N; i++)
		memo[i][1] = 1;

	for(int i=2; i<=K; i++)
		for(int j=0; j<=N; j++)
			for (int k = 0; k <= j; k++)
			{
				memo[j][i] += memo[j - k][i - 1];
				memo[j][i] %= MOD;
			}

	return memo[N][K];
}

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

	memset(memo, -1, sizeof(memo));
	cout << Topdown(N, K) << endl;

	memset(memo, 0, sizeof(memo));
	cout << Bottomup(N, K) << endl;

	return 0;
}

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

[문제 링크]

 

1520번: 내리막 길

첫째 줄에는 지도의 세로의 크기 M과 가로의 크기 N이 빈칸을 사이에 두고 주어진다. 이어 다음 M개 줄에 걸쳐 한 줄에 N개씩 위에서부터 차례로 각 지점의 높이가 빈 칸을 사이에 두고 주어진다.

www.acmicpc.net


이 문제는 낮은 정답률에 비해 쉽게 풀 수 있었다.

정답률이 낮은 이유는 아마도 동서남북 방향을 탐색하지 않고 동쪽과 남쪽만 탐색해서 오답을 받는 경우가 많기 때문일거라 생각한다.

 

동서남북 방향을 탐색하여 높이가 낮은 지점이 있다면 그 지점에서 (M, N)으로 가는 경로의 개수를 구하고 그 값을 누적해주면 된다.

 

한 지점에서 (M, N)으로 가는 경로의 개수는 항상 같기 때문에 이를 메모이제이션해주면 중복탐색을 최소화할 수 있다.


#include <iostream>
#include <cstring>
using namespace std;

int M, N, arr[501][501], memo[501][501];
int dy[4] = { 1,-1,0,0 }, dx[4] = { 0,0,1,-1 };

int Topdown(int hereY, int hereX)
{
	if (hereY > M || hereY == 0 || hereX > N || hereX == 0)
		return 0;
	if (hereY == M && hereX == N)
		return 1;

	int& ret = memo[hereY][hereX];
	if (ret != -1) return ret;

	ret = 0;

	for (int i = 0; i < 4; i++)
		if (arr[hereY][hereX] > arr[hereY + dy[i]][hereX + dx[i]])
			ret += Topdown(hereY + dy[i], hereX + dx[i]);

	return ret;
}

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

	for (int i = 1; i <= M; i++)
		for (int j = 1; j <= N; j++)
			cin >> arr[i][j];

	memset(memo, -1, sizeof(memo));
	cout << Topdown(1, 1) << endl;

	return 0;
}

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

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

백준 1890번: 점프  (0) 2020.07.03
백준 2225번: 합분해  (0) 2020.07.03
백준 11054번: 가장 긴 바이토닉 부분 수열  (0) 2020.07.02
백준 11722번: 가장 긴 감소하는 부분수열  (0) 2020.07.02
백준 1904번: 01타일  (0) 2020.07.01

[문제 링크]

 

11054번: 가장 긴 바이토닉 부분 수열

첫째 줄에 수열 A의 크기 N이 주어지고, 둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ Ai ≤ 1,000)

www.acmicpc.net


얼핏 보기엔 복잡하고 어려워보이는 문제였지만, LIS와 LDS를 구하는 dp함수를 구현하면 간단히 풀 수 있는 문제였다.

 

중심점 here을 기준으로 arr[1] ~ arr[here] 까지 LIS + arr[here] ~ arr[N] 까지 LDS - 1 이 바이토닉 부분 수열의 길이가 되고, 중심점 here을 1~N으로 했을 때 가장 큰 값이 문제의 정답이 된다.


#include <iostream>
using namespace std;

int N, arr[1001], LIS[1001], LDS[1001];

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

int Topdown_LIS(int here)
{
	int& ret = LIS[here];
	if (ret != 0) return ret;

	ret = 1;

	for (int i = 1; i < here; i++)
		if (arr[i] < arr[here])
			ret = max(ret, Topdown_LIS(i) + 1);
	
	return ret;
}

int Topdown_LDS(int here)
{
	int& ret = LDS[here];
	if (ret != 0) return ret;

	ret = 1;

	for (int i = here + 1; i <= N; i++)
		if (arr[i] < arr[here])
			ret = max(ret, Topdown_LDS(i) + 1);

	return ret;
}

int Bottomup_LIS(int here)
{
	for (int i = 1; i <= here; i++)
	{
		LIS[i] = 1;
		for (int j = 1; j < i; j++)
			if (arr[i] > arr[j])
				LIS[i] = max(LIS[i], LIS[j] + 1);
	}

	return LIS[here];
}

int Bottomup_LDS(int here)
{
	for (int i = N; i >= here; i--)
	{
		LDS[i] = 1;
		for (int j = i + 1; j <= N; j++)
			if (arr[i] > arr[j])
				LDS[i] = max(LDS[i], LDS[j] + 1);
	}

	return LDS[here];
}

int main(void)
{
	cin >> N;

	for (int i = 1; i <= N; i++)
		cin >> arr[i];
	
	// top-down
	int res = 1;
	for (int i = 1; i <= N; i++)
		res = max(res, Topdown_LIS(i) + Topdown_LDS(i) - 1);
	cout << res << endl;
	
	// bottom-up
	res = 1;
	for (int i = 1; i <= N; i++)
		res = max(res, Bottomup_LIS(i) + Bottomup_LDS(i) - 1);
	cout << res << endl;

	return 0;
}

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

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

백준 2225번: 합분해  (0) 2020.07.03
백준 1520번: 내리막 길  (0) 2020.07.03
백준 11722번: 가장 긴 감소하는 부분수열  (0) 2020.07.02
백준 1904번: 01타일  (0) 2020.07.01
백준 11051번: 이항 계수 2  (0) 2020.06.30

[문제 링크]

 

11722번: 가장 긴 감소하는 부분 수열

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

www.acmicpc.net


https://onlytrying.tistory.com/80 LIS와 같은 유형의 문제였다.


#include <iostream>
using namespace std;

int N, arr[1001], memo[1001];

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

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

	ret = 1;
	for (int i = start + 1; i <= N; i++)
		if (arr[start] > arr[i])
			ret = max(ret, Topdown(i) + 1);

	return ret;
}

int Bottomup()
{
	int res = 1;
	for (int i = 1; i <= N; i++)
	{
		memo[i] = 1;
		for (int j = 1; j < i; j++)
			if (arr[i] < arr[j])
				memo[i] = max(memo[i], memo[j] + 1);
		res = max(res, memo[i]);
	}

	return res;
}

int main(void)
{
	cin >> N;

	for (int i = 1; i <= N; i++)
		cin >> arr[i];
	
	int res = 1;
	for (int i = 1; i <= N; i++)
		res = max(res, Topdown(i));
	cout << res << endl;

	cout << Bottomup() << endl;

	return 0;
}

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

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

백준 1520번: 내리막 길  (0) 2020.07.03
백준 11054번: 가장 긴 바이토닉 부분 수열  (0) 2020.07.02
백준 1904번: 01타일  (0) 2020.07.01
백준 11051번: 이항 계수 2  (0) 2020.06.30
백준 2133번: 타일 채우기  (0) 2020.06.26

[문제 링크]

 

1904번: 01타일

지원이에게 2진 수열을 가르쳐 주기 위해, 지원이 아버지는 그에게 타일들을 선물해주셨다. 그리고 이 각각의 타일들은 0 또는 1이 쓰여 있는 낱장의 타일들이다. 어느 날 짓궂은 동주가 지원이��

www.acmicpc.net


https://onlytrying.tistory.com/81 타일 문제와 비슷한 유형의 문제라고 생각한다.

 

N == 1일 때 만들 수 있는 타일은 1 한가지 존재하고,

N == 2일 때 만들 수 있는 타일은 00, 11 두가지 존재한다.

N == 3일 때 만들 수 있는 타일은 N==1일 때 만들 수 있는 타일 뒤에 N==2일 때 만들 수 있는 타일을 붙인 모양(100, 111)과 새롭게 만들 수 있는 001이 추가되어 세가지 존재하게 된다.

 

이를 통해 앞서 많이 풀어봤던 타일채우기 문제들과 동일한 유형의 문제임을 알 수 있다.

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

 

dp(N) = dp(N-2) * 2 + dp(N-3)


#include <iostream>
using namespace std;

const int MOD = 15746;
int memo[1000001];

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

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

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

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

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

	return memo[N];
}

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

	cout << Topdown(N) << endl;

	cout << Bottomup(N) << endl;
	
	return 0;
}

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

[문제 링크]

 

11051번: 이항 계수 2

첫째 줄에 \(N\)과 \(K\)가 주어진다. (1 ≤ \(N\) ≤ 1,000, 0 ≤ \(K\) ≤ \(N\))

www.acmicpc.net


이항 계수를 구하는 점화식을 이용하여 쉽게 풀 수 있는 문제였다.


#include <iostream>
#include <cstring>
using namespace std;

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

int Topdown(int N, int K)
{
	if (K == 1) return N;
	if (K == 0 || K == N) return 1;

	int& ret = memo[N][K];
	if (ret != -1) return ret;

	return ret = ((Topdown(N - 1, K - 1) % MOD) + (Topdown(N - 1, K) % MOD)) % MOD;
}

int Bottomup(int N, int K)
{
	memo[1][0] = memo[1][1] = 1;

	for (int i = 2; i <= N; i++)
		for (int j = 0; j <= i; j++)
			memo[i][j] = ((memo[i - 1][j] % MOD) + (memo[i - 1][j - 1] % MOD)) % MOD;

	return memo[N][K];
}

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

	memset(memo, -1, sizeof(memo));
	cout << Topdown(N, K) << endl;

	memset(memo, 0, sizeof(memo));
	cout << Bottomup(N, K) << endl;

	return 0;
}

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

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

백준 11722번: 가장 긴 감소하는 부분수열  (0) 2020.07.02
백준 1904번: 01타일  (0) 2020.07.01
백준 2133번: 타일 채우기  (0) 2020.06.26
백준 2294번: 동전 2  (0) 2020.06.25
백준 11048번: 이동하기  (0) 2020.06.25

+ Recent posts