有关计数问题的DP
划分数
题目:放苹果_牛客网
描述
有
n个无区别的物品,将它们划分成不超过
m组,求出划分方法数模
M的余数。
(1<=m<=n<=1000,2<=M<=10000)
输入
n = 4
m = 3
M = 10000
输出
4
题解
定义
dp[i][j]=j的i划分的总数
把
n件物品分成至多
m份
= 把
n件物品分成至多
m−1份
+把
n件物品分成刚好
m份
对“把
n件物品分成刚好
m份”进行分析:把每一份都减一后就像相当于把
n−m件物品分成至多
m份
所以有:
dp[i][j]=dp[i][j−i]+dp[i−1][j]
代码
#include <bits/stdd++/h>
#define maxn 30
#define _for(i, a) for(LL i = 0; i < (a); i++)
#define _rep(i, a, b) for(LL i = (a); i <= (b); i++)
using namespace std;
typedef long long LL;
const LL inf = 0x3f3f3f3f;
int dp[maxn][maxn];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n, m;
while (cin >> n >> m) {
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
_rep(i, 1, m) {
_for(j, n + 1) {
if (j >= i) {
dp[i][j] = dp[i][j - i] + dp[i - 1][j];
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
cout << dp[m][n] << "\n";
}
return 0;
}
多重集组合数
描述
有
n种物品,第
i种物品有
ai个,不同种类的物品可以互相区分,但相同种类的物品无法区分。从这些物品中取出
m个的话,有多少种取法?求出方案数模
M的余数。
(1<=n<=1000,1<=m<=1000,1<=ai<=1000,2<=M<=10000)
输入
n = 3
m = 3
a = {1, 2, 3}
M = 10000
输出
6
题解
定义
dp[i][j]=前i种物品中取出j个的组合总数
所以为了从前
i中物品中取出
j个,可以从前
i−1种物品中取出
j−k个,再从第
i种物品拿出
k个
于是
dp[i][j]=∑k=0min(i,a[i])dp[i−1][j−k]
这时候时间复杂度是
O(n∗m2),显然还是可以优化的
首先把求和式展开,注意到在k的上限里有一个取最小值,所以这里要分两种情况
- 当
j>a[i]时
dp[i][j]=dp[i−1][j]+dp[i−1][j−1]+...+dp[i−1][j−a[i]]=dp[i−1][j]+dp[i][j−1]−dp[i][j−1−a[i]]
- 当
j<=a[i]时
dp[i][j]=dp[i−1][j]+dp[i−1][j−1]+...+dp[i−1][0]=dp[i−1][j]+dp[i][j−1]
这时候时间复杂度就降到
O(n∗m)了