洛谷-5056 【模板】插头dp

题目描述
给出nm的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?
输入格式
第1行,n,m(2<=n,m<=12)
从第2行到第n+1行,每行一段字符串(m个字符),"
“表不能铺线,”."表必须铺
输出格式
输出一个整数,表示总方案数

输入输出样例
输入 #1
4 4
**…


输出 #1
2

输入 #2
4 4



输出 #2
6

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<iterator>
#include<cstdlib>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define mo 590027
using namespace std;
int n,m,mapx[20][20]={0},endx,endy;
ll all_ans=0,dp[2][600000]={0};
int bits[28]={0};
int state[2][600000]={0},tots[2]={0};
int pre=1,cnt=0;
struct hash_table{
    int pre,to;
}idx[600000]={0};
int ptr[600000]={0},at=0;
inline void readx(int& x){
    x=0; int k=1; register char ch=0;
    while (ch<'0' || ch>'9') { ch=getchar(); if (ch=='-') k=-1; }
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar(); }
    x*=k;
}
inline void readit(){
    readx(n);readx(m);
    char cht=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
        cht=0; while (cht!='.' && cht!='*') cht=getchar();
        if (cht=='.') { mapx[i][j]=1; endx=i; endy=j; }
    }
}
inline void init_bits() { for (int i=1;i<=25;i++) bits[i]=(i<<1); }
inline void hah(int sta,ll val){
    int key=sta%mo;
    for (int prex=ptr[key];prex;prex=idx[prex].pre) if (state[cnt][idx[prex].to]==sta){
        dp[cnt][idx[prex].to]+=val;
        return;
    }
    tots[cnt]++;
    state[cnt][tots[cnt]]=sta;
    dp[cnt][tots[cnt]]=val;
    idx[++at].pre=ptr[key];
    idx[at].to=tots[cnt];
    ptr[key]=at;
}
inline void DP(){
    tots[cnt]=1; dp[cnt][1]=1;
    state[cnt][1]=0;
    for (int i=1;i<=n;i++){
    for (int j=1;j<=tots[cnt];j++) state[cnt][j]<<=2;
    for (int j=1;j<=m;j++){
        at=0; memset(ptr,0,sizeof ptr);
        swap(pre,cnt);
        tots[cnt]=0;
        register int nowsta,is_d,is_r; register ll nowans;
        for (int k=1;k<=tots[pre];k++){
            nowsta=state[pre][k],nowans=dp[pre][k];
            is_d=(nowsta>>bits[j])%4,is_r=(nowsta>>bits[j-1])%4;
            if(!mapx[i][j]){
                if((!is_d) && (!is_r)) hah(nowsta,nowans);
            }
            else if ((!is_d) && (!is_r)){
                if(mapx[i+1][j] && mapx[i][j+1])
                    hah(nowsta+(1<<bits[j-1])+2*(1<<bits[j]),nowans);
            }
            else if ((!is_d) && is_r){
                if(mapx[i+1][j]) hah(nowsta,nowans);
                if(mapx[i][j+1]) hah(nowsta-is_r*(1<<bits[j-1])+is_r*(1<<bits[j]),nowans);
            }
            else if (is_d && (!is_r)){
                if(mapx[i][j+1]) hah(nowsta,nowans);
                if(mapx[i+1][j]) hah(nowsta-is_d*(1<<bits[j])+is_d*(1<<bits[j-1]),nowans);
            }
            else if (is_d==1 && is_r==1){
                register int count=1;
                for(int l=j+1;l<=m;l++){
                    if((nowsta>>bits[l])%4==1) count++;
                    else if ((nowsta>>bits[l])%4==2) count--;
                    if (!count){
                        hah(nowsta-(1<<bits[l])-(1<<bits[j])-(1<<bits[j-1]),nowans);
                        break;
                    }
                }
            }
            else if (is_d==2 && is_r==2){
                register int count=1;
                for(int l=j-2;l>=0;l--){
                    if((nowsta>>bits[l])%4==1) count--;
                    else if ((nowsta>>bits[l])%4==2) count++;
                    if(!count){
                        hah(nowsta-2*(1<<bits[j])-2*(1<<bits[j-1])+(1<<bits[l]),nowans);
                        break;
                    }
                }
            }
            else if (is_d==1 && is_r==2) hah(nowsta-2*(1<<bits[j-1])-(1<<bits[j]),nowans);
            else if (is_r==1 && is_d==2) if(i==endx && j==endy) all_ans+=(ll)nowans;
            }
        }
    }
}
int main(){
    readit(); init_bits();
    DP();
    printf("%lld\n",all_ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值