http://acm.hdu.edu.cn/showproblem.php?pid=4804
其实这种放骨牌的题好像也可以直接状压,不过挺难受的,还不如直接插头DP
轮廓线只需要用1和0来标记是否有拓展到右边或下边的1*2骨牌,然后再把已经放了的1*1压进状态里,及bit+=num*mi[m+1]就行了
注意一个细节,当你选择横着放的1*2的骨牌时,由于影响的是下一个位置,需要判断一下是否下一个位置被从上一行往下的骨牌给覆盖了。
转移就是如果当前格子被左边或者右边或者本身就不能放的话,直接把j-1和第j位变成0转移
否则说明当前格子需要放一个新的,考虑放竖的横的和单独放
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=110;
const int mod=1e9+7;
const int hs=1423333;
int n,m,now,cnt[2],mt,C,D;ll ans;
int q[2][hs+3],hd[hs+3],nxt[hs+3],mark[hs+3],mi[20];
ll dp[2][hs+3];
int a[maxl][maxl];
char s[maxl];
inline void prework()
{
scanf("%d%d",&C,&D);
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
a[i][j]=s[j]-'0';
}
for(int j=1;j<=m;j++)
a[n+1][j]=0;
for(int i=1;i<=n;i++)
a[i][m+1]=0;
}
inline void insert(int bit,ll val)
{
int u=bit%hs+1;
if(mark[u]!=mt)
hd[u]=0,mark[u]=mt;
for(int i=hd[u];i;i=nxt[i])
if(q[now][i]==bit)
{
dp[now][i]=(dp[now][i]+val)%mod;
return;
}
nxt[++cnt[now]]=hd[u];
hd[u]=cnt[now];
q[now][cnt[now]]=bit;
dp[now][cnt[now]]=val;
}
inline void mainwork()
{
int num,bit,nbit;ll val;ans=0;
cnt[now]=1;q[now][1]=0;dp[now][1]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=cnt[now];j++)
{
num=q[now][j]>>(m+1);
q[now][j]&=mi[m+1]-1;
q[now][j]*=2;
q[now][j]+=num*mi[m+1];
}
for(int j=1;j<=m;j++)
{
now^=1;++mt;cnt[now]=0;
for(int k=1;k<=cnt[now^1];k++)
{
bit=q[now^1][k];num=bit>>(m+1);val=dp[now^1][k];
if(!a[i][j] || ((bit>>j)&1) || ((bit>>(j-1))&1))
{
nbit=bit;
if((bit>>j)&1) nbit^=mi[j];
if((bit>>(j-1))&1) nbit^=mi[j-1];
insert(nbit,val);
if(i==n && j==m && C<=num && num<=D)
ans=(ans+val)%mod;
}
else
{
if(a[i][j+1] && !(bit>>(j+1)&1) && j<m)
insert(bit^mi[j],val);
if(a[i+1][j] && i<n)
insert(bit^mi[j-1],val);
if(num<D)
{
insert(bit+mi[m+1],val);
if(i==n && j==m && C<=num+1 && num+1<=D)
ans=(ans+val)%mod;
}
}
}
}
}
}
inline void print()
{
printf("%lld\n",ans);
}
int main()
{
mi[0]=1;
for(int i=1;i<=17;i++)
mi[i]=mi[i-1]<<1;
while(~scanf("%d%d",&n,&m))
{
prework();
mainwork();
print();
}
return 0;
}