알고리즘 문제(SOL)

[백준/11052/파이썬] 카드 구매하기

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

 

11052번: 카드 구매하기

첫째 줄에 민규가 구매하려고 하는 카드의 개수 N이 주어진다. (1 ≤ N ≤ 1,000) 둘째 줄에는 Pi가 P1부터 PN까지 순서대로 주어진다. (1 ≤ Pi ≤ 10,000)

www.acmicpc.net

Problem

  • 카드 팩의 가격이 주어졌을 때, N개의 카드를 구매하기 위해 민규가 지불해야 하는 금액의 최댓값을 구하는 프로그램을 작성하시오. 

조건

  • 민규는 카드의 개수가 적은 팩이더라도 가격이 비싸면 높은 등급의 카드가 많이 들어있을 것이라는 미신을 믿고 있다. 따라서, 민규는 돈을 최대한 많이 지불해서 카드 N개 구매하려고 한다. 카드가 i개 포함된 카드팩의 가격은 Pi원이다.
  •  N개보다 많은 개수의 카드를 산 다음, 나머지 카드를 버려서 N개를 만드는 것은 불가능하다. 즉, 구매한 카드팩에 포함되어 있는 카드 개수의 합은 N과 같아야 한다.

SOL

그리디?

장당 가격이 가장 높은것만 계속 사면 되지않을까? 라고 생각하기 쉽지만, 즉, 구매한 카드팩에 포함되어 있는 카드 개수의 합은 N과 같아야 한다는 조건 때문에, 반례를 잘 걸러내야한다.

 

완전탐색?

n장을 자연수 분해하는 경우의 수를 다 구해야한다. 고등학교 때, 해봤던 자연수 분할의 개념을 이용하는거 같은데, 자연수의 분할은 일반적으로 재귀적인 관계가 성립한다. P(n,k) = P(n-1,k-1) + P(1) 이런식으로 그래서, 재귀적으로 구현을 많이하는데, 재귀는 일반적으로 2^n이다. 음, 완탐으로 하면, 일단 시간초과가 난다.

 

DP

 

1장을 샀을 때, 최대값 => P[1]

2장을 샀을 때, 최대값 => P[2] or P[1] +P[1]

3장을 샀을 때, 최대값 => P[3] or P[2]+P[1] or P[1]+P[1]+P[1]

..N장을 샀을 때, 최대값 => P[N] or P[N-1]+P[1] or P[N-2] +P[2] or P[N-2] +P[1]+P[1] or P[N-3] +P[3] 

나열해보면, 재귀적인 관계가 성립한다는게 잘 보인다.

  • DP[N] = N장을 샀을 때, 최댓값
  • DP[N] = max(DP[N],DP[N-K]+P[k])

반복을 어떻게 해야하는지 잘 정해야한다.

 

예를들어, 내가 3장의 카드를 사고 싶다면, P[3] or P[2]+P[1] or P[1]+P[1]+P[1] 3가지 방법이 있을건데,

DP[2]에 이미, 2장의 카드를 사는 최댓값이 적혀있다면, P[2] , P[1]+P[1] 은 추가적으로 연산을 안해도되게 된다.

따라서, DP[i-k] +P[k] 의 관계를 형성하게 된다. 즉, DP[i-k] +P[k]를 말로 풀어쓰면, i-k장 사는 최댓값  + k장 카드 사는 가격으로 생각할 수 있다. 그리고, 이 값들 중에서 max값을 DP[i]에 저장을 해야, 최댓값이 DP[i]에 계속 저장되게 된다.

코드로 구현하면, 아래와 같아짐!

#DP ,bottom up
import sys
input = sys.stdin.readline

N = int(input())
P = [0] + list(map(int,input().split()))
DP= [0] *(N+1)
#print(price)

for i in range(1,N+1):
    for k in range(1,i+1):
        DP[i] = max(DP[i],DP[i-k]+P[k])

print(DP[N])