
계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점수를 얻게 된다.
예를 들어 <그림 2>와 같이 시작점에서부터 첫 번째, 두 번째, 네 번째, 여섯 번째 계단을 밟아 도착점에 도달하면 총 점수는 10 + 20 + 25 + 20 = 75점이 된다.
계단 오르는 데는 다음과 같은 규칙이 있다.
따라서 첫 번째 계단을 밟고 이어 두 번째 계단이나, 세 번째 계단으로 오를 수 있다. 하지만, 첫 번째 계단을 밟고 이어 네 번째 계단으로 올라가거나, 첫 번째, 두 번째, 세 번째 계단을 연속해서 모두 밟을 수는 없다.
각 계단에 쓰여 있는 점수가 주어질 때 이 게임에서 얻을 수 있는 총 점수의 최댓값을 구하는 프로그램을 작성하시오.
입력의 첫째 줄에 계단의 개수가 주어진다.
둘째 줄부터 한 줄에 하나씩 제일 아래에 놓인 계단부터 순서대로 각 계단에 쓰여 있는 점수가 주어진다. 계단의 개수는 300이하의 자연수이고, 계단에 쓰여 있는 점수는 10,000이하의 자연수이다.
제약 조건을 만족하며 얻을 수 있는 점수의 최댓값을 구하면 되는 문제이다.
가장 쉽고 직관적으로 문제를 해결하는 방법은 한 계단을 밟았을 경우, 두 계단을 밟았을 경우를 전수 검사하는 방법이다.
하지만 해당 방법은 연산의 중복으로 인해 시간 초과가 발생한다.
이러한 불필요한 연산의 중복을 제거하기 위해 DP(Dynamic Programming)이라는 기법을 사용한다.
DP는 계산된 데이터를 저장 해 두고 필요한 경우 재계산 하지 않고 저장된 데이터에 접근해 이용한다.
N번째 계단을 밟는다고 가정해 보자.
세 개의 계단을 연속으로 밟아서는 안된다고 하였으므로 N번째 계단을 밟을 경우 가능한 경우는 아래 그림과 같다.
밟을 N번째 계단을 연두색으로, 밟을 수 있는 이전 단계의 계단을 빨간색으로 나타내었다.
해당 그림을 수식으로 나타내면 다음과 같다.
위의 두 값 중 최댓값을 취하고 N번째 계단의 점수를 더하면 N번째 계단을 밟았을 때 점수의 최댓값을 구할 수 있다.
이 때 (N - 3)번째 계단과 (N - 2)번째 계단의 최댓값을 계산하여 별도의 배열에 저장해 두면 불필요한 연산을 줄일 수 있다.
import java.io.*;
public class Main {
static int[] score, memo;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 계단의 개수 입력
int N = Integer.parseInt(br.readLine());
score = new int[N];
memo = new int[N];
// 계단별 점수 입력
for(int i = 0; i < N; i++) {
score[i] = Integer.parseInt(br.readLine());
}
memo[0] = score[0]; // 첫 항 설정
System.out.println(recursive(N - 1)); // 재귀함수 호출
}
// DP 수행하는 재귀 함수
public static int recursive(int n) {
if(n < 0) return 0; // 음수번째 계단은 없으므로 0 반환
if(memo[n] != 0) return memo[n]; // 이전에 계산한 값 존재하면 계산 불필요
int tmp1 = recursive(n - 3) + score[n - 1]; // 3층 이전의 값 + 1층 이전의 값
int tmp2 = recursive(n - 2); // 2층 이전의 값
// N번째 계단까지 점수의 최대는 (N - 1)최대 + N번째 계단 점수
memo[n] = Math.max(tmp1, tmp2) + score[n];
return memo[n];
}
}
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#define fastio ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr)
#define endl '\n'
using namespace std;
int *score, *memo;
// DP 수행하는 재귀 함수
int recursive(int n) {
if(n < 0) return 0; // 음수번째 계단은 없으므로 0 반환
if(memo[n] != 0) return memo[n]; // 이전에 계산한 값 존재하면 계산 불필요
int tmp1 = recursive(n - 3) + score[n - 1]; // 3층 이전의 값 + 1층 이전의 값
int tmp2 = recursive(n - 2); // 2층 이전의 값
// N번째 계단까지 점수의 최대는 (N - 1)최대 + N번째 계단 점수
memo[n] = max(tmp1, tmp2) + score[n];
return memo[n];
}
int main() {
fastio;
// 계단의 개수 입력
int N;
cin >> N;
score = new int[N]{0, };
memo = new int[N]{0, };
// 계단별 점수 입력
for(int i = 0; i < N; i++) {
cin >> score[i];
}
memo[0] = score[0]; // 첫 항 설정
cout << recursive(N - 1) << endl; // 재귀함수 호출
return 0;
}
lateinit var score: Array<Int>
lateinit var memo: Array<Int>
// DP 수행하는 재귀 함수
fun recursive(n: Int): Int {
if(n < 0) return 0 // 음수번째 계단은 없으므로 0 반환
if(memo[n] != 0) return memo[n] // 이전에 계산한 값 존재하면 계산 불필요
var tmp1 = recursive(n - 3) + score[n - 1] // 3층 이전의 값 + 1층 이전의 값
var tmp2 = recursive(n - 2) // 2층 이전의 값
// N번째 계단까지 점수의 최대는 (N - 1)최대 + N번째 계단 점수
memo[n] = Math.max(tmp1, tmp2) + score[n]
return memo[n]
}
fun main(args: Array<String>) = with(System.`in`.bufferedReader()) {
// 계단의 개수 입력
var N = readLine().toInt()
score = Array<Int>(N, {0})
memo = Array<Int>(N, {0})
// 계단별 점수 입력
for(i in 0 until N) {
score[i] = readLine().toInt()
}
memo[0] = score[0] // 첫 항 설정
println(recursive(N - 1)) // 재귀함수 호출
}
from sys import stdin
score = None
memo = None
# DP 수행하는 재귀 함수
def recursive(n):
global score, memo
if n < 0:
return 0 # 음수번째 계단은 없으므로 0 반환
if memo[n] != 0:
return memo[n] # 이전에 계산한 값 존재하면 계산 불필요
tmp1 = recursive(n - 3) + score[n - 1] # 3층 이전의 값 + 1층 이전의 값
tmp2 = recursive(n - 2) # 2층 이전의 값
# N번째 계단까지 점수의 최대는 (N - 1)최대 + N번째 계단 점수
memo[n] = max(tmp1, tmp2) + score[n]
return memo[n]
def main():
# 계단의 개수 입력
N = int(stdin.readline())
global score, memo
score = [0] * N
memo = [0] * N
# 계단별 점수 입력
for i in range(N):
score[i] = int(stdin.readline())
memo[0] = score[0] # 첫 항 설정
print(recursive(N - 1)) # 재귀함수 호출
if __name__ == "__main__":
main()
import Foundation
var score: Array<Int>!
var memo: Array<Int>!
// DP 수행하는 재귀 함수
func recursive(n: Int)-> Int {
if(n < 0) {
// 음수번째 계단은 없으므로 0 반환
return 0
}
if(memo[n] != 0) {
// 이전에 계산한 값 존재하면 계산 불필요
return memo[n]
}
var tmp1 = recursive(n: n - 3) + score[n - 1] // 3층 이전의 값 + 1층 이전의 값
var tmp2 = recursive(n: n - 2) // 2층 이전의 값
// N번째 계단까지 점수의 최대는 (N - 1)최대 + N번째 계단 점수
memo[n] = max(tmp1, tmp2) + score[n]
return memo[n]
}
func main() {
// 계단의 개수 입력
var N = Int(readLine()!)!
score = Array<Int>(repeating: 0, count: N)
memo = Array<Int>(repeating: 0, count: N)
// 계단별 점수 입력
for i in 0..<N {
score[i] = Int(readLine()!)!
}
memo[0] = score[0] // 첫 항 설정
print(recursive(n: N - 1)) // 재귀함수 호출
}
main()
