1. 程式人生 > >【HDU4689】Derangement(動態規劃)

【HDU4689】Derangement(動態規劃)

tdi 位置 while 表示 ++ fine cstring pri com

【HDU4689】Derangement(動態規劃)

題面

Vjudge
給定一個\(+-\)組成的字符串,長度為\(n\)
如果第\(i\)位是\(+\),那麽\(p_i>i\),否則\(p_i<i\)
求滿足上述條件的\([1,n]\)的排列個數。

題解

如果所有數都滿足\(p_i<i\),那麽就是\(RabbitNumbering\)
考慮如何暴力,我們可以直接大力容斥。
枚舉一部分的\(+\),強制將其變為沒有任何限制,那麽方案數還是上面\(RabbitNumbering\)那題,最後容斥計算即可。
但是這樣子復雜度吃不下。
換種方式考慮。
如果我們從左往右依次填數,設\(f[i][j]\)

表示當前填到了第\(i\)個位置,有\(j\)\(+\)不滿足。
假設當前位置是\(+\),考慮如何轉移
第一種是用當前的\(i\)填掉前面的一個\(+\),這樣一定能夠滿足。
或者讓他填掉後面隨便一個\(-\)號。
如果當前位置是一個\(-\)的話,我們必須要填掉,
因為前面空出來了\(j\)\(+\),所以有\(j\)個數可以用來填這個\(-\),組合算一下。
那麽,當前的這個\(i\)的抉擇和前面一樣,所以大力轉移一下就好了。
忽然發現我的代碼是這道題所有提交記錄裏面最短的???

#include<cstdio>
#include<cstring>
#define ll long long
ll f[22][22];
char ch[22];
int n;
int main()
{
    while(scanf("%s",ch+1)!=EOF)
    {
        n=strlen(ch+1);memset(f,0,sizeof(f));f[0][0]=1;
        for(int i=1;i<=n;++i)
            if(ch[i]=='+')
                for(int j=1;j<=i;++j)f[i][j]=f[i-1][j-1]+f[i-1][j]*j;
            else
                for(int j=0;j<=i;++j)f[i][j]=f[i-1][j+1]*(j+1)*(j+1)+f[i-1][j]*j;
        printf("%lld\n",f[n][0]);
    }
}

【HDU4689】Derangement(動態規劃)