与えられた3つの整数 n
、 m
および k
。正の整数の配列の最大要素を見つけるには、次のアルゴリズムを検討してください。
次のプロパティを持つ配列arrを構築する必要があります。
arr
正確にn
整数を持ってい ます。1 <= arr[i] <= m
どこ(0 <= i < n)
。- 上記のアルゴリズムをに適用する
arr
と、値search_cost
はに等しくなりk
ます。
上記の条件下で 配列を構築する 方法の数を返し arr
ます。答えが大きくなる可能性があるため、答えはmoduloで計算する必要があります 10^9 + 7
。
例1:
入力:n = 2、m = 3、k = 1
出力:6
説明:可能な配列は[1、1]、[2、1]、[2、2]、[3、1]、[3、2] [3、3]です
例2:
入力:n = 5、m = 2、k = 3
出力:0
説明:上記の条件を満足する可能性のあるアレイはありません。
例3:
入力:n = 9、m = 1、k = 1
出力:1
説明:可能な配列は[1、1、1、1、1、1、1、1、1]のみです
例4:
入力:n = 50、m = 100、k = 25
出力:34549172
説明:1000000007を法とする答えを計算することを忘れないでください
例5:
入力:n = 37、m = 17、k = 7
出力:418930126
制約:
1 <= n <= 50
1 <= m <= 100
0 <= k <= n
O(N * K * M ^ 2)動的プログラミング
状態:
dp [i] [j] [maxV]:arr [0、i](長さi + 1)の配列を作成する方法の数。jの検索コストとmaxVの最大数。
遷移:
1. if the current value V is not a new max number, then V must be in range [1, maxV], otherwise V will be a new max number. So the current number has maxV different choices, each choice corresponds to the number of ways of shorter length by 1, same search cost and max number, dp[i - 1][j][maxV]. So dp[i][j][maxV] += dp[i - 1][j][maxV] * maxV;
2. if the current value V is a new max number, then V only has one option, maxV. We can append maxV to all arrays of shorter length by 1, smaller search cost by 1 and max value smaller than maxV to get array of longer length by 1, bigger search cost by 1 and max number maxV. So dp[i][j][maxV] = Sum of (dp[i - 1][j - 1][smallerV]), smallerV in [1, maxV - 1].
Init:
For array of 1 element, there is 1 way for each different number choice with a search cost of 1. This is true because maximum value is initially set to < 0, so any number choice will incur an update. dp[0][1][maxV] = 1, maxV in [1, m].
Answer: Sum of dp[n - 1][k][maxV], maxV in [1, m]. For each valid array, its max value must be in [1, m], so the final answer is the sum over all max value possibilities for length n and search cost k.
class Solution { public int numOfArrays(int n, int m, int k) { int mod = (int)1e9 + 7; long[][][] dp = new long[n][k + 1][m + 1]; for(int maxV = 1; maxV <= m; maxV++) { dp[0][1][maxV] = 1; } for(int i = 1; i < n; i++) { for(int j = 1; j <= k; j++) { for(int maxV = 1; maxV <= m; maxV++) { //newly added number is not a new max value dp[i][j][maxV] = (dp[i][j][maxV] + dp[i - 1][j][maxV] * maxV) % mod; //newly added number is a new max value for(int smallerV = 1; smallerV < maxV; smallerV++) { dp[i][j][maxV] = (dp[i][j][maxV] + dp[i - 1][j - 1][smallerV]) % mod; } } } } long ans = 0; for(int v = 1; v <= m; v++) { ans = (ans + dp[n - 1][k][v]) % mod; } return (int)ans; } }
The for loop to for case: newly added number is a new max value does redundant work as it sums up the same prefixes dp[i - 1][j - 1][smallerV] repeatly. To optimize this, we can create a prefix sum array to save the previous summation results. This reduces the runtime of computing newly added number is a new max value case from O(M) to O(1).
class Solution { public int numOfArrays(int n, int m, int k) { int mod = (int)1e9 + 7; long[][][] dp = new long[n][k + 1][m + 1]; long[][][] ps = new long[n][k + 1][m + 1]; for(int maxV = 1; maxV <= m; maxV++) { dp[0][1][maxV] = 1; ps[0][1][maxV] = ps[0][1][maxV - 1] + 1; } for(int i = 1; i < n; i++) { for(int j = 1; j <= k; j++) { for(int maxV = 1; maxV <= m; maxV++) { //newly added number is not a new max value dp[i][j][maxV] = (dp[i][j][maxV] + dp[i - 1][j][maxV] * maxV) % mod; //newly added number is a new max value dp[i][j][maxV] = (dp[i][j][maxV] + ps[i - 1][j - 1][maxV - 1]) % mod; ps[i][j][maxV] = (ps[i][j][maxV - 1] + dp[i][j][maxV]) % mod; } } } long ans = 0; for(int v = 1; v <= m; v++) { ans = (ans + dp[n - 1][k][v]) % mod; } return (int)ans; } }