본문 바로가기

알고리즘/백준

[11048] 이동하기(C++/DP)

1. 문제

www.acmicpc.net/problem/11048

 

11048번: 이동하기

준규는 N×M 크기의 미로에 갇혀있다. 미로는 1×1크기의 방으로 나누어져 있고, 각 방에는 사탕이 놓여져 있다. 미로의 가장 왼쪽 윗 방은 (1, 1)이고, 가장 오른쪽 아랫 방은 (N, M)이다. 준규는

www.acmicpc.net

2. 접근 방법

1) 이동방향이 (r+1, c), (r, c+1), (r+1, c+1) 이므로 사이클은 생기지 않는다. (1,1)->(N,M)까지 갈 수 있는 경우의 수를 구하려고 했다.

 

2) candy[i][j]가 (i,j)번째 도착했을 때 얻을 수 있는 최대 사탕 개수라고 할 때, candy[i][j]는 미로의 (i,j)번째 방에 놓인 사탕의 수 + (i,j)에 오기 직전까지 가장 많이 얻을 수 있는 사탕의 개수이다. 이 때,(i,j)에 올 수 있는 경우의 수는 (i-1,j)에서 오는 경우, (i,j-1)에서 오는 경우, (i-1,j-1)에서 오는 경우 총 3가지 이다. 이를 수식으로 표현하면

candy[i][j] = map[i][j] + max({candy[i][j-1],candy[i-1][j], candy[i-1][j-1]})이다.

 

3) top-down방식으로 (N-1,M-1)에서 시작해서 (0,0)까지 재귀 함수를 돌릴 수 있다. 이 때 memorization을 사용하여 한번 구한 candy[i][j]값은 값으로 저장해두면 효율적이다.

 

3) 사실 top-down방식은 쉬운데, bottom-up으로 생각하기 어려웠다. 그런데 나중에 이동 방향을 다시 생각해보니 우측, 아래측, 우측하단(대각선)이므로 2중 for문의 방향과 일치했다..!! 그래서 이번 문제의 경우는 bottom-up으로도 같은 수식으로 문제를 풀 수 있다.. 대신 이동 방향이 사방으로 펼쳐져있으면 bottom-up으로 풀면 안된다..!! 조심!!

 

3. 실수했던 부분 & 처음 알았던 부분

1) 2차원 벡터를 전역변수로 선언하고, main에서 (N,M)만큼 메모리 할당하는 법

: vector<int> tmp(M,0);

  v.assign(N,tmp);

  

2) 이동 방향이 (오른쪽, 아래쪽, 오른쪽아래) 3개가 있으면 bottom-up으로도 쉽게 풀 수 있다.

4. 코드

더보기
/*
candy[i][j] : (i,j)에 도착했을 때 가질 수 있는 사탕 개수의 최대값
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int N,M;
vector<vector<int> > map(1000,vector<int>(1000,0));
vector<vector<int> > candy(1000,vector<int>(1000,-1));

int go( int x,int y) {

	if( x< 0 || y < 0 ) return 0;
	if(candy[x][y] != -1) return candy[x][y];
	
	candy[x][y] = map[x][y] + max({go(x-1,y),go(x-1,y-1),go(x,y-1)});
	return candy[x][y];
}

int main() {
	cin >> N >> M;	
	
	for(int i=0;i<N;i++)
	 for(int j=0;j<M;j++)
	  	cin >> map[i][j];

	cout << go(N-1,M-1) <<endl;
	return 0;
}

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

[9012]괄호  (0) 2020.12.10
[2805]나무자르기(C++)  (0) 2020.12.08
[2468]안전 영역(C++)  (0) 2020.10.18
[1261] 알고스팟(C++)  (0) 2020.10.16
[2606] 바이러스  (0) 2020.10.10