POJ 1739 Tony's Tour

题意:给定n*m的带障碍格子图,求从左下角走到右下角的哈密顿路的总数。

题解:更进一步的插头DP,主要解法可以参照我的上篇日志:http://blog.csdn.net/tmeteorj/article/details/8635090。

改动之处在于单向插头,上篇中用01和10分别代表左右括号,这道题则需要加11作为单向括号(因为它在括号序列状态中是没有与之匹配的括号的)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAX=10007;
typedef long long LL;
struct HT
{
    int head,hash[MAX],nxt[MAX];
    LL cnt[MAX];
    void init()
    {
        head=-1;
        memset(hash,-1,sizeof(hash));
        memset(nxt,-1,sizeof(nxt));
        memset(cnt,0,sizeof(cnt));
    }
    void insert(int st,LL ct)
    {
        LL pos=st%MAX;
        LL ad=1;
        while(hash[pos]!=st&&hash[pos]!=-1)
        {
            pos+=ad*ad;
            ad++;
            if(pos>=MAX)pos%=MAX;
        }
        if(hash[pos]==-1)
        {
            hash[pos]=st;
            cnt[pos]=ct;
            nxt[pos]=head;
            head=pos;
        }
        else
            cnt[pos]+=ct;
    }
} dp[2];
int n,m;
char mp[14][14];
int findright(int st,int pos)
{
    int tp,p=pos;
    for(int dep=1; dep!=0; p+=1)
    {
        tp=((st>>((m-p+1)*2))&3)%4;
        switch(tp)
        {
        case 0:
            break;
        case 1:
            dep++;
            break;
        case 2:
            dep--;
            break;
        }
        if(dep==0)
            break;
    }
    return 3<<((m-p+1)*2);
}
int findleft(int st,int pos)
{
    int tp,p=pos;
    for(int dep=1; dep!=0; p-=1)
    {
        tp=((st>>((m-p+1)*2))&3)%4;
        switch(tp)
        {
        case 0:
            break;
        case 1:
            dep--;
            break;
        case 2:
            dep++;
            break;
        }
        if(dep==0)
            break;
    }
    return 3<<((m-p+1)*2);
}
LL getans(int fg)
{
    LL pos=0,ad=1;
    while(dp[fg].hash[pos]!=0&&dp[fg].hash[pos]!=-1)
    {
        pos+=ad*ad;
        ad++;
        if(pos>=MAX)pos%=MAX;
    }
    if(dp[fg].hash[pos]==-1)
    {
        return 0ll;
    }
    else
        return dp[fg].cnt[pos];
}
int main()
{
    while(scanf("%d%d",&n,&m),n||m)
    {
        for(int i=1; i<=n; i++)scanf(" %s",mp[i]+1);
        int fg1=1,fg2,ex,ey;
        fg1=0,fg2=1;
        dp[fg2].init();
        dp[fg2].insert(0,1ll);
        for(int i=1; i<=n; i++)
        {
            fg1=!fg1;
            fg2=!fg2;
            dp[fg2].init();
            for(int tp=dp[fg1].head; tp!=-1; tp=dp[fg1].nxt[tp])
            {
                if(dp[fg1].hash[tp]&3)
                    continue;
                dp[fg2].insert(dp[fg1].hash[tp]>>2,dp[fg1].cnt[tp]);
            }
            for(int j=0; j<m; j++)
            {
                fg1=!fg1;
                fg2=!fg2;
                dp[fg2].init();
                int a=3<<(2*(m-j)),b=3<<(2*(m-j-1));
                for(int pos=dp[fg1].head; pos!=-1; pos=dp[fg1].nxt[pos])
                {
                    int st=dp[fg1].hash[pos];
                    LL ct=dp[fg1].cnt[pos];
                    int x=((st&a)>>(2*(m-j)))%4,y=((st&b)>>(2*(m-j-1)))%4;
                    if(i==n)
                    {
                        if(j==0)
                        {
                            if(y)
                                dp[fg2].insert(st&(~b)|findright(st,j+3),ct);
                            else
                                dp[fg2].insert(st|b,ct);
                            continue;
                        }
                        else if(j==m-1)
                        {
                            if((x==3||y==3)&(x!=3||y!=3))
                                dp[fg2].insert(st&(~a)&(~b),ct);
                            continue;
                        }
                    }
                    if(mp[i][j+1]=='#')
                    {
                        if(x||y)
                            continue;
                        dp[fg2].insert(st,ct);
                    }
                    else if(x)
                    {
                        if(y)
                        {
                            if(x==1&&y==1)
                                dp[fg2].insert((st&(~a)&(~b))^findright(st,j+3),ct);
                            else if(x==2&&y==1)
                                dp[fg2].insert(st&(~a)&(~b),ct);
                            else if(x==2&&y==2)
                                dp[fg2].insert((st&(~a)&(~b))^findleft(st,j),ct);
                            else if(x==3)
                                dp[fg2].insert((st&(~a)&(~b))|findright(st,j+3),ct);
                            else if(y==3)
                                dp[fg2].insert((st&(~a)&(~b))|findleft(st,j),ct);
                        }
                        else
                        {
                            dp[fg2].insert(st,ct);
                            dp[fg2].insert(st&(~a)|(x<<(2*(m-j-1))),ct);
                        }
                    }
                    else if(y)
                    {
                        dp[fg2].insert(st,ct);
                        dp[fg2].insert(st&(~b)|(y<<(2*(m-j))),ct);
                    }
                    else
                        dp[fg2].insert(st|(1<<(2*(m-j)))|(2<<(2*(m-j-1))),ct);
                }
            }
        }
        printf("%lld\n",getans(fg2));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值