排列组合 HDU1521 (指数型生成函数模板)

题意:

有n种物品,并且知道每种物品的数量。要求从中按顺序选出m件物品的方案数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有"AB","BA"两种。

思路:

显然是一个多重集排列数问题,需要用指数型生成函数。

多重集排列数:

k种物品,个数分别为\(a_1,a_2,a_3...a_k\)\(\sum{a_i}=n\)则按顺序摆放所有物品的方案数为\(n!/\prod{a_i!}\)

\(G(x)=\prod(1+x/1!+x^2/2!+x^3/3!+....+x^{a_i}/a_i!)\)

最后得到\(G(x)=1+a_1*x/1!+a_2*x^2/2!+....a_p*x^p/p!\)

其中\(a_p\)就是从所有物品中选出p件物品的方案数,因此实际上只要求其中的一个系数(即\(a_m\))(实际上全部都能求出来),利用二维DP求解就可以(背包问题),如果空间过大可以用滚动数组。

#include <bits/stdc++.h>
using namespace std;
double fac[15];
void getfac() {
	fac[0] = 1;
	for (int i = 1; i <= 10; i++) {
		fac[i] = fac[i - 1] * i;
	}
}
double dp[15][15];
int num[15];
int main() {
	getfac();
	int n, m;
	while(cin >> n >> m){
        memset(dp,0,sizeof(dp));
        for (int i = 1; i <= n; i++) {
            cin >> num[i];
        }
        for (int i = 0; i <= num[1]; i++) {
            dp[1][i] = 1.0 / fac[i];
        }
        for (int i = 2; i <= n; i++) {//枚举第几项
            for (int j = 0; j <= m; j++) {//枚举之前选了几个
                for (int k = 0; k <= num[i] && j + k <= m; k++) {//枚举当前项选几个
                    dp[i][j + k] += dp[i - 1][j] / fac[k];
                }
            }
        }
        double ans = dp[n][m] * fac[m];
        printf("%.0f\n", ans);
	}
}
posted @ 2020-02-06 16:57  UCPRER  阅读(231)  评论(0编辑  收藏  举报