【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\n",dp[0][m]);
    }
    return 0;
}
posted @ 2020-08-11 18:14  oblivionl  阅读(105)  评论(0编辑  收藏  举报