状态压缩+升维dp——UCF 2016 I

两种dp状态的设置法

比较坑的一个地方:不论是哪种设置法,最后都要特判一下所有骨牌相同的情况,这种情况要把最后的结果/2

  因为此时每种骨牌排列,都可以将每块骨牌逆过来,还是合法的,基于我们设置的状态,这样就多统计了一倍,所以要/2

  只要有一块骨牌和其他不同,上述情况就不成立

此外:当s[i]==t[i]时,我们只挑选一个计算贡献即可

1.dp[S][i]:当前状态集合为S,末尾是i

/*
dp[01][1]=1
dp[01][2]=1
dp[10][1]=1
dp[10][2]=1
dp[11][1]=1
dp[11][2]=1
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define mod 1000000007

ll dp[1<<17][17],s[20],t[20],n;

int main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        int flag=0;
        for(int i=0;i<n;i++){
            cin>>s[i]>>t[i];
            if(!(s[i]==s[0] && t[i]==t[0] || s[i]==t[0] && t[i]==s[0]))
                flag=1;
        }
        if(!flag){
            ll ans=1;
            for(int i=1;i<=n;i++)
                ans=ans*i%mod;
            cout<<ans<<'\n';continue;
        }
        memset(dp,0,sizeof dp);
        
        dp[0][0]=1; 
        for(int S=0;S<(1<<n);S++){//当前状态是S 
            for(int i=0;i<n;i++)if(!(S>>i & 1)){
                //把第i块放在最后的方案数
                if(S==0){
                    dp[S|(1<<i)][s[i]]=(dp[S|(1<<i)][s[i]]+dp[S][0])%mod;
                    if(s[i]!=t[i])
                        dp[S|(1<<i)][t[i]]=(dp[S|(1<<i)][t[i]]+dp[S][0])%mod;
                }
                else  {
                    dp[S|(1<<i)][s[i]]=(dp[S|(1<<i)][s[i]]+dp[S][t[i]])%mod;
                    if(s[i]!=t[i])
                        dp[S|(1<<i)][t[i]]=(dp[S|(1<<i)][t[i]]+dp[S][s[i]])%mod;
                }
            }
        }
        
        long long ans=0;
        for(int i=1;i<=6;i++)
            ans=(ans+dp[(1<<n)-1][i])%mod;
        cout<<ans<<'\n';               
    }
}

2.dp[S][i][0|1] 状态集合S,最后一个是骨牌i,0|1正放,逆放

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define mod 1000000007

ll dp[1<<17][17][2],s[20],t[20],n;

int main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        int flag=0;
        for(int i=0;i<n;i++){
            cin>>s[i]>>t[i];
            if(!(s[i]==s[0] && t[i]==t[0] || s[i]==t[0] && t[i]==s[0]))
                flag=1;
        }
        if(!flag){
            ll ans=1;
            for(int i=1;i<=n;i++)
                ans=ans*i%mod;
            cout<<ans<<'\n';continue;
        }
        
        memset(dp,0,sizeof dp);
        
        dp[0][0][0]=1; 
        for(int S=0;S<(1<<n);S++){//当前状态是S 
            if(S==0){
                for(int i=0;i<n;i++){
                    dp[S|(1<<i)][i][0]=(dp[S|(1<<i)][i][0]+1)%mod;
                    dp[S|(1<<i)][i][1]=(dp[S|(1<<i)][i][1]+1)%mod;
                }
                continue;
            }
            for(int i=0;i<n;i++)if(S>>i & 1){//最后一块是i 
                for(int j=0;j<n;j++)if(!(S>>j & 1)){
                    if(s[j]==t[i])
                        dp[S|(1<<j)][j][0]=(dp[S|(1<<j)][j][0]+dp[S][i][0])%mod;
                    if(s[j]==s[i] && s[i]!=t[i])
                        dp[S|(1<<j)][j][0]=(dp[S|(1<<j)][j][0]+dp[S][i][1])%mod;
                
                    if(t[j]==t[i])
                        dp[S|(1<<j)][j][1]=(dp[S|(1<<j)][j][1]+dp[S][i][0])%mod;
                    if(t[j]==s[i] && s[i]!=t[i])
                        dp[S|(1<<j)][j][1]=(dp[S|(1<<j)][j][1]+dp[S][i][1])%mod;
                }
            }
        }
        
        long long ans=0;
        for(int i=0;i<n;i++){
            ans=(ans+dp[(1<<n)-1][i][0])%mod;
            if(s[i]!=t[i])
                ans=(ans+dp[(1<<n)-1][i][1])%mod;
        }
        cout<<ans<<'\n';
    }
}

 

posted on 2020-03-30 23:06  zsben  阅读(172)  评论(0编辑  收藏  举报

导航