【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;
}