BZOJ 2331 地板(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2331

题意:给出一个n*m的地面。有些是障碍。用L型的地板砖铺满。有多少种方案。

思路:用0表示没有插头,用1表示有插头且可以拐弯,用3表示有插头但是不能再拐弯了。

设有m列,轮廓线为[0,m]。对于格子(i,j),设左插头x上插头y,那么转移有:

(1)x=0,y=0:此时如图1-0,有三种转移,分别是1-1,1-2,1-3;

(2)x!=0,y!=0:此时只有当x=y=1时可以转移,就是合并插头,如图3-0;

(3)x和y中只有一个不是0。不妨设y=0,x!=0,那次此时若x=3,可以延续或者停止;若x=1,可以合并或者可以拐弯。

 

struct node
{
    int st[N],cnt[N],size;
    int head[HashSize],next[N];
    
    void init()
    {
        size=0;
        clr(head,-1);
    }
    
    void add(int S,int x)
    {
        int t=S%HashSize;
        int i;
        for(i=head[t];i!=-1;i=next[i])
        {
            if(st[i]==S)
            {
                cnt[i]=(cnt[i]+x)%mod;
                return;
            }
        }
        st[size]=S;
        cnt[size]=x;
        next[size]=head[t];
        head[t]=size++;
    }
};


node f[2];
int pre,cur;
int n,m;
char s[105][105];




void init()
{
    RD(n,m);
    int i,j;
    FOR1(i,n) RD(s[i]+1);
    char s1[105][105];
    if(m>n)
    {
        FOR1(j,m) FOR1(i,n) s1[j][i]=s[i][j];
        swap(n,m);
        FOR1(i,n) FOR1(j,m) s[i][j]=s1[i][j];
    }
}


int getBit(int st,int k)
{
    return (st>>(k+k))&3;
}


int set0(int st,int k)
{
    return st&(~(3<<(k+k)));
}


int set1(int st,int k)
{
    st=set0(st,k);
    return st|(1<<(k+k));
}


int set3(int st,int k)
{
    return st|(3<<(k+k));
}


void add(int S,int x)
{
    f[cur].add(S,x);
}


void DP(int i,int j)
{
    int k,x,y,S,cnt;
    FOR0(k,f[pre].size)
    {
        S=f[pre].st[k];
        cnt=f[pre].cnt[k];
        if(j==1)
        {
            if(getBit(S,m)) continue;
            S=S<<2;
        }
        x=getBit(S,j-1);
        y=getBit(S,j);
        S=set0(S,j-1);
        S=set0(S,j);
        if(s[i][j]=='*')
        {
            if(x||y) continue;
            add(S,cnt);
            continue;
        }
        if(!x&&!y)
        {
            add(set1(S,j),cnt);
            add(set1(S,j-1),cnt);
            S=set3(S,j-1);
            S=set3(S,j);
            add(S,cnt);
        }
        else if(x&&y)
        {
            if(x==1&&y==1) add(S,cnt);
        }
        else if(x||y)
        {
            if(x==3) add(set3(S,j),cnt),add(S,cnt);
            else if(x==1) add(set1(S,j),cnt),add(set3(S,j-1),cnt);
            
            if(y==3) add(set3(S,j-1),cnt),add(S,cnt);
            else if(y==1) add(set1(S,j-1),cnt),add(set3(S,j),cnt);
        }
    }
}


int main()
{
    init();
    pre=0; cur=1;
    f[pre].init(); f[pre].add(0,1);
    int i,j;
    FOR1(i,n) FOR1(j,m)
    {
        f[cur].init();
        DP(i,j);
        swap(pre,cur);
    }
    int ans=0;
    FOR0(i,f[pre].size) if(f[pre].st[i]==0) ans=f[pre].cnt[i];
    PR(ans);
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值