【題解】HDU4689 Derangement(有技巧的計數DP)

【題解】HDU4689 Derangement(有技巧的計數DP)

傳送門

呵呵沒告訴我多測組數,然後\(n\le 20,7000\mathrm{ms}\)我寫了個狀壓上去T了

題目大意:

要你求錯排的方案數,但要求\(i\)位上的數比\(i\)大/小。大小關係用正負號告訴你,讀入一個字符串。

\(O(n2^n)\)

\(dp(s)\)表示已經放了\(|s|\)個數進去,放的數佔滿了\(s\)中的位置的方案數

轉移太顯然直接貼代碼

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define DE(s) cerr<<(#s)<<"="<<(s)<<endl;
#define lowbit(x) ((x)&-(x))

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(!isdigit(c))f|=c==45,c=getchar();
      while(isdigit(c)) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
const int maxn=20;
int n,U;
ll dp[1<<maxn];
char c[maxn];

int main(){
      while(~scanf("%s",c)){
        n=strlen(c);
        dp[0]=1;
        U=(1<<n)-1;
        for(int t=1;t<=U;++t){
          dp[t]=0;
          int cnt=0;
          for(int g=t;g;g-=lowbit(g)) ++cnt;
          for(int i=0,g=t;i<n&&g;++i){
            if(g>>i&1){
                  if(c[i]=='+'&&cnt>i+1) dp[t]+=dp[t^(1<<i)];
                  if(c[i]=='-'&&cnt<i+1) dp[t]+=dp[t^(1<<i)];
                  g^=1<<i;
            }
          }
        }
        printf("%lld\n",dp[U]);
      }
      return 0;
}

過不了 別想了

\(O(n^2)\)

考慮+號是一個後綴性的匹配,-號是一個前綴型的匹配。也就是說我們不可能直接把數給選好,要在後面再進行選擇。這啓發我可以設這樣的狀態:

\(dp(i,j)\)表示已經考慮前\(i\)個符號,但是需要從後面拉來\(j\)\(>i\)數來湊齊前面的\(“+”\)

當前是負號:

  • 當前位置上的數拿來匹配前面的+
    \[ dp(i,j)\leftarrow dp(i-1,j+1)((i-1)-(sum(+)-(j+1))-sum(-))(j+1) \]

  • 當前位置上的數不匹配前面的+
    \[ dp(i,j)\leftarrow dp(i-1,j)((i-1)-(sum(+)-j)-sum(-)) \]

當前是正號

  • 當前位置上的數拿來匹配前面一個+
    \[ dp(i,j)\leftarrow dp(i-1,j)j \]

  • 當前位置上的數不匹配
    \[ dp(i,j)\leftarrow dp(i-1,j-1) \]

複雜度\(O(n^2)\)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define DE(s) cerr<<(#s)<<"="<<(s)<<endl
#define lowbit(x) ((x)&-(x))

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(!isdigit(c))f|=c==45,c=getchar();
      while(isdigit(c)) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
const int maxn=25;
int n,U;
ll dp[maxn][maxn];
char c[maxn];


int main(){
      while(~scanf("%s",c+1)){
        n=strlen(c+1);
        memset(dp,0,sizeof dp);
        dp[0][0]=1;
        int cnt_minus=0,cnt_plus=0;
        for(int t=1;t<=n;++t){
          if(c[t]=='-') {
            for(int i=0;i<=cnt_plus;++i)
                  dp[t][i]=dp[t-1][i+1]*(t-1-(cnt_plus-(i+1))-cnt_minus)*(i+1ll)*(i+1<=cnt_plus)+dp[t-1][i]*(t-1-(cnt_plus-i)-cnt_minus);
            ++cnt_minus;
          }
          else {
            for(int i=0;i<=cnt_plus+1;++i){
                  if(i) dp[t][i]+=dp[t-1][i-1];
                  dp[t][i]+=dp[t-1][i]*i;
            }
            ++cnt_plus;
          }
        }
        printf("%lld\n",dp[n][0]);
      }
      return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章