DP

문제


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

예를 들어 <그림 2>와 같이 시작점에서부터 첫 번째, 두 번째, 네 번째, 여섯 번째 계단을 밟아 도착점에 도달하면 총 점수는 10 + 20 + 25 + 20 = 75점이 된다.

계단 오르는 데는 다음과 같은 규칙이 있다.

  1. 계단은 한 번에 한 계단씩 또는 두 계단씩 오를 수 있다. 즉, 한 계단을 밟으면서 이어서 다음 계단이나, 다음 다음 계단으로 오를 수 있다.
  2. 연속된 세 개의 계단을 모두 밟아서는 안 된다. 단, 시작점은 계단에 포함되지 않는다.
  3. 마지막 도착 계단은 반드시 밟아야 한다.

따라서 첫 번째 계단을 밟고 이어 두 번째 계단이나, 세 번째 계단으로 오를 수 있다. 하지만, 첫 번째 계단을 밟고 이어 네 번째 계단으로 올라가거나, 첫 번째, 두 번째, 세 번째 계단을 연속해서 모두 밟을 수는 없다.

각 계단에 쓰여 있는 점수가 주어질 때 이 게임에서 얻을 수 있는 총 점수의 최댓값을 구하는 프로그램을 작성하시오.

풀이


두가지 방식으로 풀었다.

  1. DP의 정석대로 점화식을 구하는 방법. DP 배열에는 누적된 최댓값을 저장한다.
  2. 이중 배열을 선언하여 두번째 배열로 연속 여부를 판단한다. 0인 경우는 한칸을 뛴 경우고, 1인 경우는 두칸을 뛴 경우다. 반복문을 돌 때마다 0인 경우, 1인 경우를 전부 구한다. 따라서 0인 경우 케이스를 따질 때, 두번째 배열이 0인 경우는 연속해서 3번 뛴 케이스기떄문에 고려하지 않는다. 그리고 1인 경우는 2칸 뛴 경우이므로 몇 칸을 뛰었는지 상관없이 최댓값을 구해서 넣으면 된다. 이 때 MAX 함수를 사용할 때 세개의 값을 비교하고 싶으면 “{ }” 안에 넣어야한다.

코드


사용언어: C++

첫번째 방법

#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

int dp[300 + 1];
int stair[300 + 1];

int DP(int n)
{
    dp[0] = stair[0];
    dp[1] = max(stair[1], stair[0] + stair[1]);
    dp[2] = max(stair[0] + stair[2], stair[1] + stair[2]);

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

int main()
{
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++)
    {
        scanf("%d", &stair[i]);
    }
    cout << DP(n) << endl;
    return 0;
}

두번째 방법

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;

int dp[300][2];
int stair[300];

int DP(int n)
{ //0은 1칸, 1는 2칸
    dp[0][0] = stair[0];
    dp[1][0] = stair[0] + stair[1];
    dp[1][1] = stair[1];

    for (int i = 2; i < n; i++)
    { //연속되면 안되는 경우
        dp[i][0] = max(dp[i][0], dp[i - 1][1] + stair[i]);
        dp[i][1] = max({dp[i][1], dp[i - 2][0] + stair[i], dp[i - 2][1] + stair[i]});
    }

    return (dp[n - 1][0] > dp[n - 1][1] ? dp[n - 1][0] : dp[n - 1][1]);
}

int main()
{
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++)
    {
        scanf("%d", &stair[i]);
    }
    cout << DP(n) << endl;
    return 0;
}

출처