[狀壓dp]JZOJ P3632——舞伴

Description

N 個男孩,N 個女孩,男孩和女孩可能是朋友,也可能不是朋友。現在要組成N 對舞伴,要求每對舞
伴都是一男一女,且他們是朋友。
統計不同配對方案的數量,因爲結果很大,所以只要求除以M 的餘數。

Input

第1 行,2 個整數N,M。接下來N 行,每行N 個整數Aij,表示第i 個男孩和第j 個女孩的關係。如果他們是朋友,則Aij = 1,否則Aij = 0。

Output

1 個整數,表示所求的值。

Sample Input

3 1000000000
1 1 1
1 1 1
1 1 1

Sample Output

6

Data Constraint

• 對於50% 的數據,N <= 9;
• 對於100% 的數據,1 <= N <= 20, 1 <= M <= 10^9; 0 <= Aij <= 1。

題解

快速過了一遍題目,瞟了一眼n<=20
咦!完全存得下2^20,然後就走上了一條不歸路
一開始碼題時,怕強行被數據卡不過,小心翼翼的打起來只有一維的方程(設f[x]爲當前女生匹配的狀態爲x的方案數)
後面越碼越不對勁,想來想去沒有思路怎麼去轉移方程,整個人都虛了
無奈之下。。只好ctrl A+ctrl x,重新推二維的轉移方程
結果,幾下就推出來了,還算了算複雜度發現只是O(n(n+(1<<n)n)),完全不慫,心理默默的哭泣(要是當時早打就不用浪費我一個小時的時間了)
我們設f[i][x]爲前i個男生,當前女生匹配狀態爲x的方案數
然後我們就可以枚舉男生和女生可能得到的狀態(也就是0~1<<n-1)
再判斷這個女生是否被匹配過和男生和女生是否可以匹配
狀態轉移方程就是: f[i][s|two[a[j]-1]=(f[i][s|two[a[j]-1]+f[i-1][s])%mo
然後發現這個方程裏只存再i和i-1,強行轉換爲滾動數組

代碼

#include<cstdio>
#include<cstring>
int x,n,m,a[21],two[21],f[2][1<<20],w,l;
using namespace std;
int main()
{
    freopen("perm.in","r",stdin);
    freopen("perm.out","w",stdout);
    scanf("%d%d",&n,&m);
    two[0]=1; f[0][0]=1;
    for(int i=1;i<=n;i++) two[i]=two[i-1]*2;
    for(int i=1;i<=n;i++)
    {
        x=1^x;
        l=0;
        memset(f[x],0,sizeof(f[x]));
        for(int j=1;j<=n;j++) 
        {
            scanf("%d",&w);
            if (w==1) a[++l]=j;
        }
        for(int s=0;s<two[n];s++)
            if(f[1^x][s])
                for(int j=1;j<=l;j++)
                    if(!(s&(two[a[j]-1])))
                        f[x][s|two[a[j]-1]]=(f[x][s|two[a[j]-1]]+f[1^x][s])%m;
    }
    printf("%d",f[x][two[n]-1]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章