【HDU1521】排列组合

题目链接

排列组合

题目描述

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

输入格式

每组输入数据有两行,第一行是二个数(n),(m)((1 le m),(n le 10)),表示物品数,第二行有(n)个数,分别表示这(n)件物品的数量。

输出格式

对应每组数据输出排列数。(任何运算不会超出(2^{31})的范围)

样例输入

2 2
1 1

样例输出

2

题解

其他人的题解上写的都是用母函数做,我学了半天也没学会,最后直接去看代码,发现好像就是dp,所以就用dp的方式理解了一下。
题目要求去(m)个数的排列方案数。
我们用(dp[i][j])表示前(i)个数中选了(j)个数的排列方案数。
那么转移方程就是:
(dp[i][j]=dp[i-1][j-k]*f[j]/(f[j-k]*f[k]))
(k)表示第(i)个数选择了多少个,
(f[i])表示(i)的阶乘。
接下来解释一下转移方程:
当原来的一个排列方案有(i)个数,这时候加入(k)个数,不考虑加入的数相同的情况下,那么方案数就是(prod_{j=i+1}^{i+k}j)
接下来我们考虑到加入的数相同,那么加入了(k)个数,多算的方案数就是(prod_{j=1}^{k}j)
最后我们考虑到dp中(i)的影响只有一层,所以我们可以优化掉一维空间。
打完了之后没看到有好多组数据,好dark╥﹏╥。
上代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long a[19];
long long dp[2][19];
long long f[19];
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        f[0]=1;
        for(int j=1;j<=m;j++)
            f[j]=f[j-1]*j;
        for(int j=1;j<=n;j++)
            scanf("%lld",&a[j]);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int j=1;j<=n;j++){
            for(int k=0;k<=a[j];k++){
                for(int i=0;i+k<=m;i++){
                    dp[1][i+k]+=dp[0][i]*f[i+k]/(f[i]*f[k]);
                }
            }
            for(int i=0;i<=m;i++)
                {dp[0][i]=dp[1][i];dp[1][i]=0;}
        }
        printf("%lld
",dp[0][m]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/linjiale/p/13477434.html