有关计数问题的DP

有关计数问题的DP

划分数

题目:放苹果_牛客网

描述

n n 个无区别的物品,将它们划分成不超过 m m 组,求出划分方法数模 M M 的余数。 ( 1 < = m < = n < = 1000 , 2 < = M < = 10000 ) (1<=m<=n<=1000, 2<=M<=10000)

输入

n = 4
m = 3
M = 10000

输出

4

题解

定义 d p [ i ] [ j ] = j i dp[i][j] = j的i划分的总数

n n 件物品分成至多 m m = = n n 件物品分成至多 m 1 m-1 + + n n 件物品分成刚好 m m

对“把 n n 件物品分成刚好 m m 份”进行分析:把每一份都减一后就像相当于把 n m n-m 件物品分成至多 m m

所以有: d p [ i ] [ j ] = d p [ i ] [ j i ] + d p [ i 1 ] [ j ] 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);
	//freopen("in.txt", "r", stdin);

	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 n 种物品,第 i i 种物品有 a i a_i 个,不同种类的物品可以互相区分,但相同种类的物品无法区分。从这些物品中取出 m m 个的话,有多少种取法?求出方案数模 M M 的余数。 ( 1 < = n < = 1000 , 1 < = m < = 1000 , 1 < = a i < = 1000 , 2 < = M < = 10000 ) (1<=n<=1000, 1<=m<=1000, 1<=a_i<=1000,2<=M<=10000)

输入

n = 3
m = 3
a = {1, 2, 3}
M = 10000

输出

6

题解

定义 d p [ i ] [ j ] = i j dp[i][j] = 前i种物品中取出j个的组合总数

所以为了从前 i i 中物品中取出 j j 个,可以从前 i 1 i-1 种物品中取出 j k j-k 个,再从第 i i 种物品拿出 k k

于是 d p [ i ] [ j ] = k = 0 m i n ( i , a [ i ] ) d p [ i 1 ] [ j k ] dp[i][j]=\sum_{k=0}^{min(i,a[i])}{dp[i-1][j-k]}

这时候时间复杂度是 O ( n m 2 ) O(n*m^{2}) ,显然还是可以优化的

首先把求和式展开,注意到在k的上限里有一个取最小值,所以这里要分两种情况

  • j > a [ i ] j>a[i]
    d p [ i ] [ j ] = d p [ i 1 ] [ j ] + d p [ i 1 ] [ j 1 ] + . . . + d p [ i 1 ] [ j a [ i ] ] = d p [ i 1 ] [ j ] + d p [ i ] [ j 1 ] d p [ i ] [ j 1 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 ] j<=a[i]
    d p [ i ] [ j ] = d p [ i 1 ] [ j ] + d p [ i 1 ] [ j 1 ] + . . . + d p [ i 1 ] [ 0 ] = d p [ i 1 ] [ j ] + d p [ i ] [ j 1 ] 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 ) O(n*m)

发布了163 篇原创文章 · 获赞 54 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/101724210
今日推荐