排列组合 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
", ans);
	}
}
原文地址:https://www.cnblogs.com/ucprer/p/12269304.html