【HDU1521】排列組合
阿新 • • 發佈:2020-08-11
排列組合
題目描述
有\(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; }