HDU4689Derangement (動態規劃)

bigbigship發表於2015-08-17

題目連結:傳送門 

題意:

對於一個由1~n組成的長度為n的序列來說它有n!種排法,我們定義初始的排列為1,2,3,...,n對於後續的排列如果a[i]>i則用'+'表示。a[i]<i用'-'表示,給定一個長度為n的由'+'與'-'組成的字串,問滿足這個條件的排列有多少種。

分析:

設dp[i][j]表示前i個字元中有j個'+'沒有排,'-'排完了的方案數。那麼我們可以根據字元的正負分成兩種情況來考慮。

1.a[i]='+':那麼一種情況就是前面有j-1個加號沒有放,而且這個加號也不放,或者前面已經有j個加號沒有放,那麼我們這個加號如果也不放的話就要把這個數放在前面j個加號沒有放的位置上,有j種可能。

因此這時dp[i][j]=dp[i-1][j-1]+dp[i-1][j]*j.

2.a[i]='-':同樣的一種情況就是前面已經有j+1個加號,我們需要把前面這個數放在前面的加號沒有放的位置,又要從前面加號沒有放的位置選一個放在這個位置一共j*j種方案,還有一種是如果前面有j個加號沒有放的話我們就只需要從這j個數種選一個放在這個位置上即可。,因此此時dp[i][j]=dp[i-1][j+1]*j*j+dp[i-1][j]*j;


程式碼如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 30;

typedef long long LL;

LL dp[maxn][maxn];

char s[maxn];

int main()
{
    while(~scanf("%s",s)){
        int len = strlen(s);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1LL;
        for(int i=1;i<=len;i++){
            if(s[i-1]=='+'){
                for(int j=1;j<=len;j++){
                    dp[i][j]+=dp[i-1][j-1];
                    dp[i][j]+=dp[i-1][j]*(LL)j;
                }
            }
            if(s[i-1]=='-'){
                for(int j=1;j<=len;j++){
                    dp[i][j-1]+=dp[i-1][j]*(LL)j*j;
                    dp[i][j]+=dp[i-1][j]*(LL)j;
                }
            }
        }
        printf("%I64d\n",dp[len][0]);
    }
    return 0;
}


相關文章