[BOJ] 로또 2758
![[BOJ] 로또 2758](https://onlinejudgeimages.s3-ap-northeast-1.amazonaws.com/images/big-square.png)
BOJ 로또 2758
문제 설명
선영이는 매주 엄청난 돈을 로또에 투자한다. 선영이가 하는 로또는 1부터 m까지 숫자 중에 n개의 수를 고르는 로또이다.
이렇게 열심히 로또를 하는데, 아직까지 한 번도 당첨되지 않은 이유는 수를 고를 때 각 숫자는 이전에 고른 수보다 적어도 2배가 되도록 고르기 때문이다.
예를 들어, n=4, m=10일 때, 선영이는 다음과 같이 고를 수 있다.
1 2 4 8
1 2 4 9
1 2 4 10
1 2 5 10
따라서 선영이는 로또를 4개 산다.
선영이는 돈이 엄청나게 많기 때문에, 수를 고르는 방법의 수 만큼 로또를 구매하며, 같은 방법으로 2장이상 구매하지 않는다.
n과 m이 주어졌을 때, 선영이가 구매하는 로또의 개수를 출력하는 프로그램을 작성하시오.
(조건: n ≤ 10, m ≤ 2000)
풀이
이전에 고른 수보다 적어도 2배가 되도록 고른다.
1을 고르면 적어도 2, 2를 고르면 적어도 4… 이렇게 한 없이 커지는 문제는 오히려 큰 수에서 시작해서 거꾸로 1까지 도달하면 좀 더 쉽게 접근할 수 있다. 관점을 바꿔서 큰 수부터 골라보자.
만약 선영이가 맨 처음 m이라는 숫자를 골랐다면 앞으로 m//2 이하의 숫자 중 n-1개의 수를 골라야 한다.
그렇다면 j 이하의 숫자 중 i개의 숫자를 고르는 경우의 수는
j를 패스하고 j-1 이하의 숫자 중 i개 + j를 고르고 j//2 이하의 숫자 중 i-1개를 고르는 경우를 합친 가지수가 될 것이다.
dp = [[0]*2001 for i in range(11)]
dp[0] = [1]*2001
for i in range(1, 11):
for j in range(1, 2001):
dp[i][j] = dp[i][j-1]+dp[i-1][j//2]
전체 답안
dp = [[0]*2001 for i in range(11)]
dp[0] = [1]*2001
for i in range(1, 11):
for j in range(1, 2001):
dp[i][j] = dp[i][j-1]+dp[i-1][j//2]
T = int(input())
for _ in range(T):
n, m = map(int, input().split())
print(dp[n][m])
마무리
DP는 점화식을 알면 구현은 간단한데… 역시나 규칙 찾는 것이 정말 어려운 것 같다.
규칙을 찾는 연습을 많이 해보자!