https://www.acmicpc.net/problem/10844

 

10844번: 쉬운 계단 수

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

www.acmicpc.net

이건 개념은 쉬웠다 근데 계속 틀렸습니다 나오길 래 코드를 뜯어봤더니 int의 범위를 초과해서 갑자기 값이 음수로 바뀌는 사태가 발생해서 100정도를 넣으면 값이 이상하게 나왔다 그래서 이코드는 중간중간에 값에다가 모듈러 연산을 해서 저장해주는 게 해결책이었다.

자 일단 첫번째 자리수 즉 수의 맨앞에 나오는 수는 0이 나오면 안된다 그렇기에 첫번째 자릿수가 0

이 될 경우의 수 는 0이다 그리고 나머지는 첫번째 자리에 나오는게 가능하니 1이다

2번째자릿수의 0은 이전자리에서 1에서 오는 방법밖에 없다 9도 마찬가지다 9로가는 방법은

이전자리의 수에서 8에서 밖에 오는 방법밖에 없다

그래서 0일때의 점화식은

dp[i][j] = dp[i - 1][j + 1];

9일때의 점화식은

dp[i][j] = dp[i - 1][j - 1];

이렇게 된다

#include <iostream>
using namespace std;
long long dp[101][101];
int main() {
	long long N;
	long long sum = 0;
	cin >> N;
	dp[1][0] = 0;
	for (int i = 1; i <= 9; i++) {
		dp[1][i] = 1;
	}
	
	for (int i = 2; i <= N; i++) {
		for (int j = 0; j <= 9; j++) {
			if (j == 0) {
				dp[i][j] = dp[i - 1][j + 1];
			}
			else if (j == 9) {
				dp[i][j] = dp[i - 1][j - 1];
			}
			else
				dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
				
			dp[i][j] = dp[i][j] % 1000000000;
		}

	}

	for (int i = 0; i <= 9; i++) {
		sum += dp[N][i];
		sum %= 1000000000;
	}
	cout << sum;
}

이 두 부분을 제외하고는 이전자리의 수에서 나보다 하나 크거나 하나 작은 곳에서 부터 올수 있으니

2개의 경우 의 수를 합쳐준다 즉

3번째 자리에 3은 2번째자리에 2와 4로부터 오는것이니 [2][4]+[2][2]를 하면 된다

그래서 이부분의 점화식은

dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];

이렇게 된다. 하지만 숫자가 너무 커지게 되니 이 연산이 끝나고 나면 모듈러 연산을 해주면 좋다

'백준(코테준비) > DP' 카테고리의 다른 글

백준 9465  (0) 2022.12.28
백준 11052  (0) 2022.12.26
백준 2156  (0) 2022.12.19
백준 1912  (0) 2022.12.19
백준 1932  (1) 2022.12.16

https://www.acmicpc.net/problem/2156

 

2156번: 포도주 시식

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

www.acmicpc.net

dp문제는 풀 때 마다 점화식이 중요하다고 생각되어진다 역시 이번 문제도 점화식인데

이번문제의 경우 기존에 올렸던과는 다르게 3개의 후보중 하나를 뽑는 방식으로 생각해주어야 한다

그이유는 이번 문제에서 핵심적인 내용이 연속으로 3개의 포도주를 못마신다는 거였으니  그거를 이용해서 풀어야 한다

1.O O X O

2.O X O O

3.X O O X

이문제는 위에처럼 3가지 경우의 수가 존재 한다 즉 N번째 와인이 존재한다고 가정했을때 N번째 와인을 마신다고 가정했을때 첫번째로 마시냐 두번째로 마시냐 혹은 안마시냐 이렇게 3개의 경우의 수가 존대 한다. 이를 점화식으로 나타내면

1은 dp[j-2]+arr[j];

2는 dp[j-3]+arr[j-1]+arr[j]

3은 dp[j-1]

이다 1은 j번째 포도주를 먹기로 가정했기 때문에 내이전은 못마시고 나보다 2개전에 있는 애를 마실 수 있다

2는 나도 마시고 내이전도 마신다고 가정한것이다 

마지막은 나를 안마셨기 때문에 나의 이전까지의 최대값만 필요하다 어차피 이렇게 우리가 3개의 식으로 나눴을 경우 3잔을 연속으로 마시는 경우의 수는 발생하지 않는다

#include <iostream>
#include <algorithm>
using namespace std;
int dp[10001];
int arr[10001];
int main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> arr[i];
	}
	dp[1] = arr[1];
	dp[2] = arr[1] + arr[2];

	for (int j = 3; j <= n + 1; j++) {
		dp[j] = dp[j - 1];
		dp[j] = max(dp[j], dp[j - 2] + arr[j]);
		dp[j] = max(dp[j], dp[j - 3] + arr[j - 1] + arr[j]);

	}
	cout << dp[n] << endl;
}

'백준(코테준비) > DP' 카테고리의 다른 글

백준 11052  (0) 2022.12.26
백준 10844  (0) 2022.12.19
백준 1912  (0) 2022.12.19
백준 1932  (1) 2022.12.16
백준 11053  (0) 2022.12.15

https://www.acmicpc.net/problem/1912

 

1912번: 연속합

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

www.acmicpc.net

얘는 살짝 만 생각하면 답이나오는 문제 였다. 문제 푸는 방법이 퍼뜩퍼뜩 났으면 좋겠는데 아직 수련이 부족해서 시간이 오래 걸린다.

이 문제는 간단하다 내이전에 나온 최댓값과 나를 더한값과 나 혼자 값중에 더큰애를 선택해주면 되는 문제였다.

그 이유는 어찌됬든 더하는 수가 양수라면 값은 증가하기 때문이다

10
10 -4 3 1 5 6 -35 12 21 -1

즉 이런식으로 입력이 주어지면

dp[1]은 -4와 6중에 선택한다

dp[2]는 3과 9중에 선택한다

dp[3]은 10과 1중에 선택한다

 10 6 9 10 15 21 -14 12 33 32

이런식으로 dp값이 바뀐다 즉 점화식은

max(dp[i-1]+arr[i],arr[i])값이 된다

#include <iostream>
using namespace std;
int arr[100000];
int dp[100000];
int max(int x,int y) {
	return x > y ? x:y;
}
int main() {
	int inputnum;
	int maxnum;
	cin >> inputnum;
	for (int i = 0; i < inputnum; i++) {
		cin >> arr[i];
	}
	dp[0]=arr[0];
	maxnum = dp[0];
	for (int i = 1; i < inputnum; i++) {
		dp[i] = max(dp[i - 1] + arr[i], arr[i]);
	}

	for (int i = 0; i < inputnum; i++) {
		if (dp[i] > maxnum) {
			maxnum = dp[i];
		}
	}
	cout << maxnum;
}

'백준(코테준비) > DP' 카테고리의 다른 글

백준 10844  (0) 2022.12.19
백준 2156  (0) 2022.12.19
백준 1932  (1) 2022.12.16
백준 11053  (0) 2022.12.15
백준 2579  (0) 2022.12.15

 

https://www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

이문제의 경우 일부러 DFS 로 풀어보았더니 시간초과가 나왔다. 이문제의 경우 목표지점까지 무조건 도착한다고 가정했기 때문에 DFS 보다 최소해를 구해주는 BFS로 푸는게 효율적이다.

DFS의 장점은 목표가 어디있는지 모를 때 도착지점에 도착하여 끝이난다고 생각되면 된다 나의 경우 깊이 우선탐색으로 도착할 수 있는 모든 경우의 수에 최솟값을 구하려다 보니 되추적 (백트래킹)방식을 사용하여 최소해를 구하려 했다

이렇게 푸니 문제가 시간초과가 발생하였다 혹시 모르니 이문제의 DFS (백트래킹) 버전도 첨부하도록 하겠다

BFS버전

#include <iostream>
#include <utility>
#include <queue>
using namespace std;
char maze[101][101] = { '0', };
int visited[101][101] = { 0, };//각 정점의 단계수를 표시하기 위함
int N = 0, M = 0;
queue<pair<int, int>>visitedqueue;
int main() {
	int startx = 0, starty = 0;
	cin >> N >> M;
	for (int i = 0; i < N; i++) {
		cin >> maze[i];
	}
    //시작점 삽입
	startx = 0;
	starty = 0;
	visited[0][0] = 1;
	visitedqueue.push({ startx, starty });
	while (!visitedqueue.empty()) {
		startx = visitedqueue.front().first;
		starty = visitedqueue.front().second;
		visitedqueue.pop();
		
		if (maze[startx][starty + 1] == '1' && visited[startx][starty + 1] == false) {
			visitedqueue.push({ startx,starty + 1 });	
			visited[startx][starty + 1] = 1 + visited[startx][starty];
		}
		if (maze[startx + 1][starty] == '1' && visited[startx + 1][starty] == false) {
			visitedqueue.push({ startx+1,starty });
			visited[startx+1][starty] = 1 + visited[startx][starty];
		}
		if (maze[startx - 1][starty] == '1' && visited[startx - 1][starty] == false) {
			visitedqueue.push({ startx-1,starty });
			visited[startx-1][starty] = 1 + visited[startx][starty];
		}
		if (maze[startx][starty - 1] == '1' && visited[startx][starty - 1] == false) {
			visitedqueue.push({ startx,starty-1 });
			visited[startx][starty - 1] = 1 +visited[startx][starty];
		}
		

	}

	cout << visited[N - 1][M - 1];

}

극단적으로 첫번째 표가 이번문제에 삽입된다고 하면 위에코드의 

visted배열은 이런식으로 자기자신의 단계를 나타내면서 확장 될것이다 3단계와 인접한  자식들은 모두 4단계로 마킹되면서 계속 단계를 확장 해 나갈것이다 이에 마지막에 몇단계에서 도착점이 나왔는지만 출력해주면 답이나온다

일단 극단적으로 이번 문제의 표를 나타내어 보면 어린식으로 자신의 자식들을 확장해나갈것이다

		
		if (maze[startx][starty + 1] == '1' && visited[startx][starty + 1] == false) {
			visitedqueue.push({ startx,starty + 1 });	
			visited[startx][starty + 1] = 1 + visited[startx][starty];
		}
		if (maze[startx + 1][starty] == '1' && visited[startx + 1][starty] == false) {
			visitedqueue.push({ startx+1,starty });
			visited[startx+1][starty] = 1 + visited[startx][starty];
		}
		if (maze[startx - 1][starty] == '1' && visited[startx - 1][starty] == false) {
			visitedqueue.push({ startx-1,starty });
			visited[startx-1][starty] = 1 + visited[startx][starty];
		}
		if (maze[startx][starty - 1] == '1' && visited[startx][starty - 1] == false) {
			visitedqueue.push({ startx,starty-1 });
			visited[startx][starty - 1] = 1 +visited[startx][starty];
		}

이 코드가 자기자신과 인접한 4방향중 아직 방문하지 않은 곳으로 가서 자기 자식임을 표시한다 표시방법은 나의 단계에서 1을 더해주는 방식으로 작성 하였다 또한 나의 자식들도 큐의 삽입함으로서 계속해서 자손들을 확인한다.

즉 극단적인 위의 표로 봤을때 큐의 값은 (0,0)->(0,1)(1,1)(1,0)->(0,2)(2,1)(2,2)(1,2)(0,0)->..... 이런식으로 큐의 값이 변화하게 된다. 그후 맨마지막에 도착지점이 몇번째인지 마크되어있을테니 이것을 그대로 출력해주면 된다.

 

DFS

DFS방식으로는 이문제를 풀 수 없다 백트래킹을 사용하지 않고서는 DFS는 도착지점에 도착하는 순간 끝나기 때문에 백트래킹을 사용하여 이전지점으로가 다른 루트를 찾아가는 방법을 사용하지만 이문제에서는 시간초과를 발생시킨다. 이에

DFS방식으로 푸는것을 추천하지 않는다

#include <iostream>
#include <utility>
#include <stack>
using namespace std;
char maze[101][101] = { '0',};
bool visited[100][100] = { false, };
int N = 0, M = 0;
int minnum = 10001;
stack<pair<int, int>> visitedvertex;
void dfs(int startx, int starty) {
	visited[startx][starty] = true;
	visitedvertex.push({ startx,starty });
	if (startx == N-1 && starty == M - 1) {
		if (visitedvertex.size() < minnum) {
			minnum = visitedvertex.size();
		}
	
	}

	else{
		if (maze[startx][starty + 1] == '1' && visited[startx][starty + 1] == false) {
			starty += 1;
			dfs(startx, starty);
			starty -= 1;
			
		}
		if (maze[startx + 1][starty] == '1' && visited[startx + 1][starty] == false) {
			startx += 1;
			dfs(startx, starty);
			startx -= 1;
			
		}
		if (maze[startx - 1][starty] == '1' && visited[startx - 1][starty] == false) {
			startx -= 1;
			dfs(startx, starty);
			startx += 1;
		
		}
		if (maze[startx][starty - 1] == '1' && visited[startx][starty - 1] == false) {
			starty -= 1;
			dfs(startx, starty);
			starty += 1;
		}	
	}
	visited[startx][starty] = false;
	visitedvertex.pop();
}
int main() {
	int startx = 0, starty = 0;
	cin >> N >> M;
	for (int i = 0; i < N; i ++) {
		cin >> maze[i];
	}
	dfs(0, 0);
	cout << minnum;

DFS방식은 재귀를 사용하여야 한다 이에 재귀를 사용하면서 스택을 섞어서 사용하였다 자신이 방문할때 스택에다가 값을 쌓아올린다. 이후 도착점에 도착했을 때 도착한 최솟값과 비교하여 작은값을 저장하여 DFS로 갈 수 있는 방법중 최솟값을 출력하는게 내가 한방법이다 DFS는 일단 불러지는 순간 자기자신을 스택에 쌓는다 그후 자신의 4방향을 탐색하여 방문한적이 없으면 쌓는다 하지만 방문할곳이 없으면 다시 스택에서 나온다 이것을 반복하여 방문한곳을 방문하지 않고 끝점 까지 도달하는 경우의 수들중 최소의 점만을 거쳐서 도착하는 값을 출력한다

7 7
1011111
1110001
1000001
1000001
1000001
1000001
1111111

이경우의 visitedvertex의 값은

0,0

0,1

1,1

.

.

.

..

6,6

순으로 쌓이고 끝점에 도달했으니 minnum값을 비교후 갱신하고

다시 자기이전으로 계속돌아가 분기가 발생한지점까지 다시 돌아간다 그러므로

0,0

0,1 까지돌아간 후

0,2

0,3

.

.

.

...

6,6 까지 간다 그후 minnum값과 비교하여 이제 모든 길을 가보았다면

 minnum값을 출력후 끝나게 된다

'백준(코테준비) > DFS,BFS' 카테고리의 다른 글

백준 1012  (0) 2022.12.29
백준 1697  (0) 2022.12.27
백준 24480  (1) 2022.12.21
백준 24479  (0) 2022.12.20
백준 1260  (1) 2022.12.17

 

https://www.acmicpc.net/problemset?sort=ac_desc&algo=7 

 

문제 - 1 페이지

 

www.acmicpc.net

이문제의 경우 깊이 우선 탐색(DFS) 너비 우선 탐색(BFS)의 차이를 알기에는 딱인 문제 였다.

일단 BFS의 경우 재귀함수를 사용하지 않고 풀어 보았다

현재 이렇게 입력값을 주었을 경우 BFS는 기준점을 기준으로 선 1개를 통해 연결 된 애들 선 2개를 통해 연결된 애들 

이런식으로 단계를 나눠준다 

이렇게 생각을 하거나 아니면

 

DFS의 경우에는 또 다르게 생각해주어야한다 비슷하게 생각해보자면 최대한 길게 선을 그어서 모든 정점을 방문하는 방법이라고 생각한다 단 다시 되돌아가기는 내가 왔던 점으로만 돌아 갈수 있다 또한 만약에 이때 다른 경로가 아직 방문되지 않았고 내가 간적 없는 정점이라면 그곳으로 간다. 즉 최대한 길게 갈려고 한다고 생각하면 된다.

 

다르게 생각하면 부모를 기준으로 내 첫째 자식의 후손들 까지 갔다가 2째 자식의 후손들 까지 쭉보고 나머지 자식들 순으로 그 자식들의 후손들까지 쭉 보는거라고 생각하면된다

이런식으로 기준점을 1로 두고 1과 선 한개로 연결된 애들과 선을 2개로 해서 갈수 있는 애들을 나눠서 생각해줄수 있다

단 밑에 그림처럼 생각할시에는 2번째 단계에서 이미 4를 방문했으므로 3단계에 있는 4에는 방문을 할 필요가 없다고 생각해주면 된다.

일단 전체 코드를 첨부후 부분 부분 설명하도록 하겠다.

#include <iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<memory.h>
using namespace std;
vector <int> vertex[10002];
vector <int> resultofDfs;
vector <int> resultofBfs;
int vertexnum;
bool visited[1001] = { false, };

//dfs 함수
void dfs(int start) {
	resultofDfs.push_back(start);
	visited[start] = 1;
	for (int i = 0; i < vertex[start].size(); i++) {
		if (visited[vertex[start][i]] == true){
			continue;
		}
		dfs(vertex[start][i]);
	}
}
int main() {

	queue<int> Bfsqueue;
	int edgenum;
	int startvertex;
	int startvertexforDfs;
	int inputvertex;
	int inputedge;
	cin >> vertexnum;
	cin >> edgenum;
	cin >> startvertex;
	startvertexforDfs = startvertex;


	resultofBfs.push_back(startvertex);
	for (int i = 0; i < edgenum; i++) {
		cin >> inputvertex >> inputedge;
		vertex[inputvertex].push_back(inputedge);
		vertex[inputedge].push_back(inputvertex);
	}
	//작은것부터 나와야하니까 정렬해준다
	for (int i = 1; i <= edgenum; i++) {
		sort(vertex[i].begin(), vertex[i].end());
	}
	//DFS
	dfs(startvertexforDfs);
	for (int i = 0; i < resultofDfs.size(); i++) {
		cout << resultofDfs[i] << ' ';
	}
	cout << endl;
	// 배열 재사용 위해 초기화
	memset(visited, 0, sizeof(visited));
	//BFS 
	visited[startvertex] = true;
	for (int i = 0; i < vertexnum; i++) {
		int sizeofvertex = vertex[startvertex].size();
		for (int j = 0; j < sizeofvertex; j++) {
			if (visited[vertex[startvertex][j]] == false) {
				visited[vertex[startvertex][j]] = true;
				Bfsqueue.push(vertex[startvertex][j]);
				resultofBfs.push_back(vertex[startvertex][j]);
			}
		}
		if (Bfsqueue.empty()) {
			break;
		}
		startvertex = Bfsqueue.front();
		Bfsqueue.pop();
	}
	for (int i = 0; i < resultofBfs.size(); i++) {
		cout << resultofBfs[i] << ' ';
	}
}

맨처음 각각의 정점들이 누구랑 연결되어 있는지를 vector형태로 넣어 줄 수 있도록 만들어 주었다

만약

이런 식으로 입력받아지면

vector[1]  에 2,3,4

vecotr[2]  에 1,4

vector[3]   에 1,4

가 들어가게 된다 즉 내가 다른 누군가와 연결되었는지를 입력해주게 된다

그다음 각 정점이 방문되었는지를 확인해주는 visited 배열을 만들어 주었다

void dfs(int start) {
	resultofDfs.push_back(start);
	visited[start] = 1;
	for (int i = 0; i < vertex[start].size(); i++) {
		if (visited[vertex[start][i]] == true){
			continue;
		}
		dfs(vertex[start][i]);
	}
}

이 부분의 경우 

일단 내가 들어온 곳을 방문 했다고 마크 한다

그후 for문 첫번째 조건에서 내가 아무하고 도 연결안되어있으면 for문을 돌지 않는다 그래서 더이상 가지않고 함수를

return하고 재귀적으로 call하기 이전의 함수가 다시 경로를 찾는다.

혹은 내가 만약에 방문할 수 있는 경로가 있더라도 그곳이 방문됬었다면 방문하지 않고 다른 경로를 찾는다.

 

BFS의 코드는 재귀가 아니라 반복문으로 작성하였다.

	visited[startvertex] = true;
    for (int i = 0; i < vertexnum; i++) {
		int sizeofvertex = vertex[startvertex].size();
		for (int j = 0; j < sizeofvertex; j++) {
			if (visited[vertex[startvertex][j]] == false) {
				visited[vertex[startvertex][j]] = true;
				Bfsqueue.push(vertex[startvertex][j]);
				resultofBfs.push_back(vertex[startvertex][j]);
			}
		}
		if (Bfsqueue.empty()) {
			break;
		}
		startvertex = Bfsqueue.front();
		Bfsqueue.pop();
	}

이 부분의 경우 일단 시작점은 무조건 방문한거니 true로 마크해준다

그 이후에 나랑 연결된 애들중에 방문되지 않은애들을 큐에다가 넣어 준다 resultofBFS 는 큐는 pop을 이용해서 정보를 빼고 더하기 때문에 vector에다가 방문하는 지점을 저장해주기 위해 만들어 놓은것이다 큐에 있는 것들을 pop해주면서 pop되는 애들의 자식들도 queue에다가 넣어준다 이것을 반복해서 같은대의 자식들이 계속 큐에 넣어져서 접근한다 이후에 큐가 비게되면 모두 방문한거가 된다 순서를 보자면

5 5 3
5 4
5 2
1 2
3 4
3 1

처음 큐 3-> 1,4->2-5이런식으로 root로 부터 몇개의 선으로 갈수 있는지에 따라 level이 나누어 진다

이로서 큐를 이용해서 작성해주면 편하게 작성할 수 있다

'백준(코테준비) > DFS,BFS' 카테고리의 다른 글

백준 1012  (0) 2022.12.29
백준 1697  (0) 2022.12.27
백준 24480  (1) 2022.12.21
백준 24479  (0) 2022.12.20
백준 2178  (0) 2022.12.18

https://www.acmicpc.net/problem/1932

 

1932번: 정수 삼각형

첫째 줄에 삼각형의 크기 n(1 ≤ n ≤ 500)이 주어지고, 둘째 줄부터 n+1번째 줄까지 정수 삼각형이 주어진다.

www.acmicpc.net

이 문제의 경우 여러가지 생각을 하게 해주었다 일단 처음에 visual studio 에서 

스택 사이즈를 너무 많이 먹는다고 경고를 띄우고 강제 종료되서 나를 개빡치게 했다

이건 지역변수가 스택에 할당 되서 문제였다

그래서 문제 해결법은 그냥 전역변수에 사이즈가 큰 배열을 갔다 박으면 되는 문제였다

또한 short로 하면 이문제는 틀렸습니다가 나온다 그이유는 각각의 수의 범위들은 short로

처리가 가능하지만 이 값들의 합은 short 범위 내에 있지 않을 가능성이 크기 때문이다

이에 변수들을 int로 정의 해주면 해결이었다

dp에 bottom up방식을 사용해서 풀었다.

나는 이문제가 이전에 풀었던 2 3짜리 문제 보다 훨씬 쉬었다

이문제의 경우 총 3개의 경우의 수를 생각해주어야한다

삼각형의 왼쪽끝과 오른쪽끝 그리고 이둘다 아닐경우가 다다르다

왼쪽끝과 오른쪽끝의 경우 자신의 부모가 하나밖에 없기 때문에 확정으로 값이 나온다

왼쪽은 이렇게 dp[j][k] = tri[j][k]+ dp[j - 1][k];

오른쪽은 이렇게 dp[j][k] = tri[j][k] + dp[j - 1][k-1];

둘다 아닌경우는 dp[j][k] = max((tri[j][k] + dp[j - 1][k]), (tri[j][k] + dp[j - 1][k - 1]));

이렇게 3개로 나누어줘야한다 아니면 불필요한 범위까지 계산하게 되서 백준에서 오류를 띄워준다.

#include <iostream>
using namespace std;
int tri[501][501];
int dp[501][501];

int max(int x, int y) {
	return x > y ? x : y;
}
int main() {
	int trisize;
	cin >> trisize;
	int maxnum = 0;

	for (int i = 0; i < trisize; i++) {
		for (int j = 0; j < i + 1; j++) {
			cin >> tri[i][j];
		}
	}
	dp[0][0] = tri[0][0];
	for (int j = 1; j < trisize; j++) {
		for (int k = 0; k <= j; k++) {
			if (k == 0) {
				dp[j][k] = tri[j][k]+ dp[j - 1][k];
			}
			else if (k == j) {
				dp[j][k] = tri[j][k] + dp[j - 1][k-1];
			}
			else {
				dp[j][k] = max((tri[j][k] + dp[j - 1][k]), (tri[j][k] + dp[j - 1][k - 1]));
			}
		}

	}
	for (int i = 0; i < trisize; i++) {
		if (dp[trisize-1][i] > maxnum) {
			maxnum = dp[trisize - 1][i];
		}
	}
	cout << maxnum;
}

 

'백준(코테준비) > DP' 카테고리의 다른 글

백준 10844  (0) 2022.12.19
백준 2156  (0) 2022.12.19
백준 1912  (0) 2022.12.19
백준 11053  (0) 2022.12.15
백준 2579  (0) 2022.12.15

https://www.acmicpc.net/problem/11053

 

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

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

www.acmicpc.net

처음 풀때 잘 못 생각했었다 순열안에 수를 뽑아 증가하는 수열을 만드는 거라 생각해서

처음 부터 다시 풀어야 했다

이번 꺼는 생각하는 게 좀 어려웠다 자기 자신의 이전의 경로의 수가 dp로 들어가야한다.

10 20 10 30 20 50

이렇게 수열이 있으면 처음 이렇게 만 있을 때 증가하는 수열들은 각각 하나 씩이라고 생각하자

그 후에 내 이전의 원소들을 보면서 비교한다 내 이전의 원소가 나보다 작으면

나의 수에 내 이전 원소 까지의 증가수를 합쳐준다 

예시로 

20이 10을 만나게 되면 10은 하나짜리 증가 수열이고 20은 10보다 크니

10 20은 증가수열이다 이에 서로까지의 증가 수열 수를 더해준다 이에 20은 

2의 증가 수열을 갖는다

하지만 이 때 내 이전 원소가 나보다 작을 수 있는 경우의 수가 여러 개이므로 

내 이전까지의 증가수열 수 + 1 끼리를 비교하여 가장 큰값을 내가 갖고 있어야 한다

이에 dp[i]=max(dp[i],dp[j]+1)  이 필요하다

이에 자기자신의 값과 내 이전의 증가수열이 나를 만났을 때의 개수 와 비교해 준다

이렇게 점점 자기자신보다 작은애까지의 증가수열 + 나의 값이 최대가 되는 것을 구하는게

핵심이다

#include <iostream>
using namespace std;
int max(int x, int y)
{
	return x > y ? x : y;
}
int main() {
	int arr[1001];
	int dp[1001]; //자신 까지의 증가 수열 수
	int maxnum=0;
	int inputn;
	cin >> inputn;
	for (int i = 0; i < inputn; i++) {
		cin >> arr[i];
		dp[i] = 1;
	}

	for (int i = 1; i < inputn; i++) {
		for (int j = 0; j <= i; j++) {
			if (arr[i] > arr[j]) {
				dp[i] = max(dp[i], dp[j]+1);
			}
		}
	}
	
	for (int i = 0; i < inputn; i++) {
		if (dp[i] > maxnum)
			maxnum = dp[i];
	}

	cout << maxnum;
}

'백준(코테준비) > DP' 카테고리의 다른 글

백준 10844  (0) 2022.12.19
백준 2156  (0) 2022.12.19
백준 1912  (0) 2022.12.19
백준 1932  (1) 2022.12.16
백준 2579  (0) 2022.12.15

 

https://www.acmicpc.net/problem/2579

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

www.acmicpc.net

백준 2579 번은 dynamic programming을 사용해서 풀어야 한다

dp를 하면서 느낀거는 점화식을 세우는 게 중요한거 같다.

일단 이 문제의 경우 기준은 마지막 계단이다.

마지막 계단을 올라가는 방법은 2가지가 있다

연속된 3계단을 올라갈 수 없으니 전계단과 전전전계단을 밟고 올라오는 경우의 수와

전전계단을 밟고 마지막 계단에 도착하는 경우의 수가 있다

이경우  점화식은 2개가 존재한다

 

이를 위해 i번째 계단까지 올라왔을 때의 최댓값을 저장해줄 dp 배열

각계단의 점수 값을 기록해줄 stair 배열을 생성해준다.

dp[i-3]+stair[i-1]+stair[i]

 

dp[i-2]+stair[i]

이렇게 2개가 있다

그리고 이점화식은 i가 3이상 일 때 부터 작동한다

이에 우리의 dp[0],dp[1],dp[2]는 반복문에 들어가기전에 미리 값을 삽입해 놓아야 한다.

#include <iostream>
using namespace std;
int max(int x, int y)
{
	return x > y ? x : y;
}
int main() {
	int inputn = 0;
	int stair[301] = { 0, };
	int dp[301] = { 0, };
	cin >> inputn;
	for (int i = 0; i < inputn; i++) {
		cin >> stair[i];
	}
	dp[0] = stair[0];
	dp[1] = max(stair[1] + dp[0],stair[1]);
	dp[2] = max(stair[2]+stair[1], dp[0]+stair[2]);

	for (int i = 3; i < inputn; i++) {
		dp[i] = max(stair[i] + stair[i - 1] + dp[i - 3], dp[i - 2] + stair[i]);
	}
	cout << dp[inputn-1];
}

'백준(코테준비) > DP' 카테고리의 다른 글

백준 10844  (0) 2022.12.19
백준 2156  (0) 2022.12.19
백준 1912  (0) 2022.12.19
백준 1932  (1) 2022.12.16
백준 11053  (0) 2022.12.15

+ Recent posts