- Grouping Options
There are n people in a row. They must all be divided into m contiguous groups from left to right. If each group must be at least as large as the group to its left, determine how many distinct ways groups can be formed. For a group to be distinct, it must differ in size for at least one group when sorted ascending. For example, [1, 1, 1, 3] is distinct from [1, 1, 1, 2] but not from [1, 3, 1, 1].
Example
Input:
8
4
Output: 5
Explanation: [1, 1, 1, 5], [1, 1, 2, 4], [1, 1, 3, 3], [1, 2, 2, 3], [2, 2, 2, 2]
Notice
1≤n,m≤200
解法1:
这个思路其实不太好想。参考的网上的答案。时间复杂度O(m^2 * n)。
dp[i][j] 表示前i个人分成j组的分法。
那么dp[i][i], i=1…n肯定是1。
对于dp[i][j]而言,我们可以把这j个组每个组分一个,那么剩下i-j个人,这i-j个人可以全部分配到最后1个组(对应dp[i-j][1]),也可以全部分配到最后2个组(对应dp[i-j][2]),…,也可以全部分配到全部的j个组(对应dp[i-j][j])。所以要用再用一个循环把这些方案都加起来就是dp[i][j]。
注意:
- for i的循环可以从1开始,也可以从2开始。但从1开始没有必要,因为j<i,那么j==0。
- for j的循环可以是j<=i,因为我们知道dp[i][i]==1,所以用j<i就可以了。
class Solution {
public:
/**
* @param n: the number of people
* @param m: the number of groups
* @return: the number of grouping options
*/
long long groupingOptions(int n, int m) {
if (n < m) return 0;
//dp[i][j] is the number of options that i people are divided into j groups
vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
for (int i = 1; i <= m; ++i) {
dp[i][i] = 1;
}
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= min(i, m); ++j) {
for (int k = 1; k <= j; ++k) {
dp[i][j] += dp[i - j][k];
}
}
}
return dp[n][m];
}
};
解法2:DP。时间复杂度O(m+n)。
思路是dp[i][j]可以分为2部分: 第1个数是1的和第1个数不是1的。
比如说n=8, m=4,那第1个数是1的情况就包括{1,1,1,5}, {1,1,2,4},{1,1,3,3},{1,2,2,3},第1个数不是1的情况就是{2,2,2,2}。
那实际上
1)第1个数是1的情况就等价于dp[7][3],即把第1个数拿掉后的情况,{1,1,5}, {1,2,4}, {1,3,3}, {2,2,3}。
2)第1个数不是1的情况就是将8分为4个数,但第1个数不是1的情况。等价于dp[4][4],即因为每个元素都>1,所以每个元素减去1,然后求dp[4][4]即可。
怎么理解呢?比如说第2种情况是{2,3,4,5},n=14,那么,我们可以给4个数每个数先分配一个1,剩下10在4个数里面分配,这就是dp[10][4]。
class Solution {
public:
/**
* @param n: the number of people
* @param m: the number of groups
* @return: the number of grouping options
*/
long long groupingOptions(int n, int m) {
if (n < m) return 0;
//dp[i][j] is the number of options that i people are divided into j groups
vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
for (int i = 1; i <= m; ++i) {
dp[i][i] = 1;
}
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= min(i, m); ++j) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - j][j];
}
}
return dp[n][m];
}
};
解法3:还是DP。
dp[i][j][k] 表示i拆除j个数,最后一个数是k有多少种方法
dp[i][j][k] = sum(dp[i - k][j - 1][x]), 1<=x<=k。
时间复杂度O(n^3 * m),非常慢。
时间复杂度计算如下:i from 1 to n, j from 1 to m, k from 1 to n, x from 1 to k。
TBD
本文探讨了一种将n个人分成m个连续组的算法问题,其中每个组的大小至少与左侧组相同。提供了三种不同的动态规划解法,详细解释了每种方法的时间复杂度和实现细节,帮助读者深入理解并解决此类问题。
6716

被折叠的 条评论
为什么被折叠?



