題目:
題意:多組輸入,給你n*m(n<=14,m<=1e3)的字符矩陣,只包含 v 和 . 其中v是障礙物。
你需要在.上塗色,使得每一行都有格子被塗色,相鄰兩列必須有一列有格子被塗色。求最終的合法方案總數%1e9+7。
思路:
一看到這個題意和n的範圍,肯定是狀壓dp。
首先,如果直接暴力狀壓的話,我們需要枚舉每一列,這一列的狀態,上一列的狀態,複雜度將會爆炸。
發現對於障礙物,我們直接開一個數組記錄這一列合法的位置即可,至於相鄰兩列必有一列塗色,我們就設dp[i]爲第i列必須塗色的合法方案數。則:
dp[0]=1
dp[i]=dp[i-1]*(2^(當前狀態&這一列的合法位置狀態 的1的個數)-1) (i=1,2...,m)
dp[i]+=dp[i-2]*(2^(當前狀態&這一列的合法位置狀態 的1的個數)-1) (i=2...,m)
最後答案就是dp[m]+dp[m-1],當然m=1的時候需要特判。
考慮到枚舉所有子狀態,我們可以直接使用容斥,將枚舉子狀態的複雜度去掉。
本題時限卡的非常嚴格,所有變量必須使用int,注意數組大小和取模,還有預處理2的冪次以及每個狀態中1的個數。
代碼:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mst(head,x,n) memset(head+1,x,n*sizeof(head[0]))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int maxn=1e3+5;
const int maxm=(1<<14)+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
const int mo=1e9+7;
int n,m,k;
int ok[maxn];
int ans,tmp;
int flag;
int dp[maxn];
char s[15][maxn];
int cnt[maxm],po[maxn];
template <typename T>
inline void read(T &X){
X=0;int w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
if(w) X=-X;
}
int cal(int s){
dp[0]=1;
rep(i,1,m){
int tmp=(po[cnt[s&ok[i]]]-1+mo)%mo;
dp[i]=1LL*dp[i-1]*tmp%mo;
if(i>1) dp[i]=(dp[i]+1LL*dp[i-2]*tmp%mo)%mo;
}
if(m==1) return dp[m];
else return (dp[m]+dp[m-1])%mo;
}
int main(){
/*
#ifdef ONLINE_JUDGE
#else
freopen("D:/Temp/in.txt", "r", stdin);
#endif
*/
po[0]=1;
rep(i,1,20) po[i]=(po[i-1]<<1)%mo;
cnt[0]=0;
rep(i,1,maxm-1) cnt[i]=cnt[i>>1]+(i&1);
int T,cas=1;
//read(T);
while(scanf("%d%d",&n,&m)!=EOF)
{
//read(n);read(m);
rep(i,0,n-1){
scanf("%s",s[i]+1);
}
rep(j,1,m){
ok[j]=0;
rep(i,0,n-1)
if(s[i][j]=='.') ok[j]|=(1<<i);
}
ans=0;
//cout<<"*";
int mm=(1<<n)-1;
rep(i,0,mm){
if((n-cnt[i])&1){
ans=(ans-cal(i)+mo)%mo;
}
else ans=(ans+cal(i))%mo;
}
printf("%d\n",ans);
}
return 0;
}