玩詐欺的小杉——異或優化的狀壓dp
阿新 • • 發佈:2020-08-10
玩詐欺的小杉
是這樣的,在小杉的面前有一個N行M列的棋盤,棋盤上有N*M個有黑白棋的棋子(一面為黑,一面為白),一開始都是白麵朝上。
小杉可以對任意一個格子進行至多一次的操作(最多進行N*M個操作),該操作使得與該格同列的上下各2個格子以及與該格同行的左右各1個格子以及該格子本身翻面。
例如,對於一個5*5的棋盤,僅對第三行第三列的格子進行該操作,得到如下棋盤(0表示白麵向上,1表示黑麵向上)。
00100
00100
01110
00100
00100
對一個棋盤進行適當的操作,使得初始棋盤(都是白麵朝上)變成已給出的目標棋盤的操作集合稱作一個解法。
小杉的任務是對給出的目標棋盤求出所有解法的總數。
每組測試資料的第一行有
接下來T個目標棋盤,每個目標棋盤N行,每行M個整數之前沒有空格且非0即1,表示目標棋盤(0表示白麵朝上,1表示黑麵朝上)
兩個目標棋盤之間有一個空行。
特別地,對於30%的資料,有1<=N,M<=15
對每組資料輸出T行,每行一個整數,表示能使初始棋盤達到目標棋盤的解法總數
輸入:
4 4 2
0010
0010
0111
0010
0010
0110
0111
0010
輸出:
1
1
【樣例解釋】
對於輸入的資料,兩個目標棋盤各有一種解法
1:
0000
0000
0010
0000
2:
1011
1101
0111
1011
其中1表示對該格進行操作,0表示不操作
分析:
這種類似棋盤的東西其實很容易讓我們想到狀壓,對比兩個方向的狀壓,我們會傾向於選擇一列一列地推過去,因為這樣問題更加簡單。所以我們可以很輕鬆地和普通的狀壓一樣打出此題,但是顯然的時間複雜度O(2n
程式碼:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define int long long #define R register #defineld long double #define debug printf("zxt\n") inline int read(){ int a=0,b=1;char c=getchar(); while(!isdigit(c)){if(c=='-')b=-1;c=getchar();} while(isdigit(c)){a=a*10+c-'0';c=getchar();} return a*b; } const int N=50; int n,m,T,a[N],b[N],al,ans; char s[N]; signed main(){ n=read();m=read(); T=read(); while(T--){ for(R int j=1;j<=m;j++){ a[j]=0; } for(R int i=1;i<=n;i++){ scanf("%s",s); for(R int j=1;j<=m;j++){ a[j]=a[j]*2+s[j-1]-'0'; } } al=(1<<n)-1;ans=0; for(a[0]=0;a[0]<=al;a[0]++){ for(R int i=0;i<=m;i++)b[i]=a[i]; for(R int i=1;i<=m;i++){ b[i]=(b[i]^(b[i-1]<<2)^(b[i-1]<<1)^b[i-1]^(b[i-1]>>1)^(b[i-1]>>2))&al; b[i+1]=(b[i-1]^b[i+1])&al; } if((b[m]&al)==0)ans++; } printf("%lld\n",ans); } return 0; }