n개의 파이를 k명의 사람에게 분배하는데 각 사람은 적어도 1개의 파이를 받고, 앞 사람보다 적게 받을 수는 없다
예를 들어 7개의 파이를 5명에게 분배할려면
1 1 1 1 3
1 1 1 2 2 는 유효하지만
2 1 1 2 1은 유효한 경우가 아니다
가능한 방법의 수는?
--------------------------------------------------------------------------------------------------------------------------------------------
dp[i][j][x]를 i번째 사람에 j개 분배하고 남은 개수가 x개라고 할때 방법의 수라고 하면
O(N^4)에 해결할 수 있다
처음에 0번 사람에게 j개를 분배하면 dp[0][j][n-j] = 1
i = 1,2,...,k-1에서 y = j, j+1,...,x개를 분배한다고 할때, i-1번째 사람이 j = 1,2,..,n개 분배받고 남은 개수가 x개라면
dp[i][y][x-y] += dp[i-1][j][x]
dp = [[[0]*(n+1) for _ in range(n+1)] for _ in range(k)]
for j in range(1,n+1):
dp[0][j][n-j] = 1
for i in range(1,k):
for j in range(1,n+1):
for x in range(1,n+1):
for y in range(j,x+1):
dp[i][y][x-y] += dp[i-1][j][x]
그러면 정답은? k-1번째 사람이 i개 받고 0개 남았을때 dp[k-1][i][0]를 모든 i = 1,2,..,n에 대해 더해주면
v = 0
for i in range(1,n+1):
v += dp[k-1][i][0]
근데 당연하지만 n = 250으로 작다고 하더라도 O(N^4)은 너무 오래걸린다 이거지
이는 사실 integer partition 문제로 유명하다
https://en.wikipedia.org/wiki/Integer_partition
Integer partition - Wikipedia
From Wikipedia, the free encyclopedia Decomposition of an integer as a sum of positive integers Young diagrams associated to the partitions of the positive integers 1 through 8. They are arranged so that images under the reflection about the main diagonal
en.wikipedia.org

dp[i][j] = 정수 i를 j개의 자연수로 분할하는 방법의 수
j개의 자연수 중 최솟값이 1이라고 하면, 나머지 j-1개에는 i-1을 분배하면 되므로 dp[i-1][j-1]
최솟값이 2이상이라고 한다면, j개에 일단 1씩 분배를 하면 사용할 수 있는 자연수는 i-j
이 i-j를 j개에 분배하면 되므로 dp[i-j][j]
따라서 dp[i][j] = dp[i-1][j-1] + dp[i-j][j]를 만족한다
약간 이 문제처럼 경우를 나눠 재귀적으로 점화식 세우는 거랑 비슷한듯?
https://deepdata.tistory.com/1398
재귀적으로 점화식 세우는 다이나믹 프로그래밍 연습1
6096번: Bulls and Cows 총 n마리의 암소와 황소를 한줄로 세울때, 두 마리의 황소 사이에 최소 k마리의 암소가 있도록 줄을 세운다고 할 때, 그러한 방법의 수를 5000011로 나눈 나머지를 구한다. 이때
deepdata.tistory.com
아무튼 dp[0][0]는 아무것도 하지 않는 방법 1가지로 dp[0][0] = 1
i = j인 경우는 i를 i개의 자연수로 분할하는 방법의 수로 1,1,1,1,...,1로 1가지밖에 없다
dp[i][i] = 1
i = 1,2,..,n에 대하여 j = 1,2,..,k에 대해 채우면 된다
여기서 j > i인 경우는 정수 i를 i보다 큰 j개의 자연수 항으로 분할하는건데 불가능하므로,
기본적으로 j <= i이다.
따라서 j = 1,2,...,min(i,k)까지 하면 된다
n = int(input())
k = int(input())
dp = [[0]*(k+1) for _ in range(n+1)]
dp[0][0] = 1
for i in range(1,n+1):
for j in range(1,min(i,k)+1):
if i == j:
dp[i][j] = 1
else:
dp[i][j] = dp[i-j][j] + dp[i-1][j-1]
print(dp[n][k])
여기서 문제 조건은 오름차순으로 분배하라했는데...
이것도 고려해야하는거 아니야?
근데 분할하기만 하면 그걸 오름차순으로 정렬하면 되는거니까 상관없다
6 = (1,2,3), (2,3,1), (2,1,3),... 이렇게 3가지로 분할하는 방법 다 있지만 전부 다 같은 경우 1가지로 센다
그래서 (2,3,1)로 분할하더라도 (1,2,3)으로 해버리면 된다 이거지
'알고리즘 > 다이나믹 프로그래밍' 카테고리의 다른 글
문자를 하나만 바꾸거나, 처음부터 연속된 k개를 바꿔서 전부 같은 문자로 바꾸는 다이나믹 프로그래밍 (0) | 2025.02.23 |
---|---|
최솟값들로 배낭을 구성하고 이들 중 최댓값을 찾아야하는 특이한 배낭문제 (0) | 2025.02.21 |
구간을 잡아야하는 matrix chain multiplication 다이나믹 프로그래밍 (0) | 2025.02.13 |
안겹치게 구간을 나누는 다이나믹 프로그래밍 테크닉 (0) | 2025.02.04 |
출발 조건이 까다로운 2차원 배열 목적지까지 이동하는 다이나믹 프로그래밍 (0) | 2025.01.27 |