1. 程式人生 > 實用技巧 >【HDU1521】排列組合

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