fzu1977-Pandora Adventure

题意

\(n\times m\) 的棋盘,有一些格子必须走,有一些必须不走,有一些可走可不走,求合法单回路个数。

\(n,m\le 12\)

分析

这题相比之前那题,多了一个可走可不走到条件,也就是说,终点不再一定是最后一个必经点。这题有两种做法。网上大部分到做法是在状态后面加一个 isend 参数,若形成回路就继续推下去,遇到新插头就说明不合法,直到推到最后一个。

我的方法比较简单。若形成回路,那么判断一下,去掉这个回路后还有没有剩下的插头,若有则形成了多回路,不合法;否则直接把这里到答案加到 ans 里面。最后还是要加上 f[0] ,因为它包含了对一个位置都不走到dp情况。

复杂度为 \(O(nm|S|)\)

代码

似乎FZU挂了,所以只是稍微测了一下,不一定是对的。

#include<bits/stdc++.h>
using namespace std;
typedef long long giant;
const int value[]={0,1,-1};
const int maxn=14;
const int maxh=1.2e5;
char s[maxn][maxn];
unordered_map<int,int> id;
int a[maxn],h[maxh],ids=0,mat[maxh][maxn],n,m;
giant F[2][maxh],*f=F[0],*g=F[1];
bool fin[maxn][maxn];
int at() {
    int ret=0;
    for (int i=m+1;i;--i) (ret+=a[i])<<=2;
    return ret;
}
void match(int mt[]) {
    static int sta[maxn];
    int top=0;
    for (int i=1;i<=m+1;++i) if (a[i]==1) sta[++top]=i; else if (a[i]==2) {
        int x=sta[top--];
        mt[x]=i,mt[i]=x;
    } else mt[i]=0;
}
void dfs(int now,int v) {
    if (v<0) return;
    if (now>m+1) {
        if (!v) {
            int x=at();
            h[id[x]=++ids]=x;
            match(mat[ids]);
        }
        return;
    }
    for (int i=0;i<3;++i) dfs(now+1,v+value[a[now]=i]);
}
int get(int d,int p) {
    return (d>>(p<<1))&3;
}
int mod(int d,int p,int x) {
    return (d&(~(3<<(p<<1))))+(x<<(p<<1));
}
giant work() {
    scanf("%d%d",&n,&m);
    memset(fin,0,sizeof fin),memset(F,0,sizeof F);
    memset(h,0,sizeof h),ids=0,id.clear();
    for (int i=1;i<=n;++i) scanf("%s",s[i]+1);
    for (int i=n;i;--i) for (int j=m;j;--j) {
        fin[i][j]=true;
        if (s[i][j]=='O') goto xj;
    }
    xj:;
    dfs(1,0);
    f[id[0]]=1;
    giant ans=0;
    for (int i=1;i<=n;++i) {
        swap(f,g),memset(f,0,sizeof(f[0])*maxh);
        for (int k=1;k<=ids;++k) {
            int d=h[k];
            if (get(d,m+1)==0) f[id[d<<2]]+=g[k];
        }
        for (int j=1;j<=m;++j) {
            swap(f,g),memset(f,0,sizeof(f[0])*maxh);
            for (int k=1;k<=ids;++k) {
                int d=h[k],x=get(d,j),y=get(d,j+1);
                if (s[i][j]=='X') {
                    if (x==0 && y==0) f[k]+=g[k];
                    continue;
                } else {
                    if (x==0 && y==0) {
                        int v=mod(mod(d,j,1),j+1,2);
                        f[id[v]]+=g[k];
                        if (s[i][j]=='*') f[k]+=g[k]; // go or not
                    } else if (x==1 && y==1) {
                        int v=mod(mod(d,j,0),j+1,0);
                        v=mod(v,mat[k][j+1],1);
                        f[id[v]]+=g[k];
                    } else if (x==2 && y==2) {
                        int v=mod(mod(d,j,0),j+1,0);
                        v=mod(v,mat[k][j],2);
                        f[id[v]]+=g[k];
                    } else if (x==0 || y==0) {
                        int v1=mod(mod(d,j,x+y),j+1,0);
                        int v2=mod(mod(d,j,0),j+1,x+y);
                        f[id[v1]]+=g[k],f[id[v2]]+=g[k];
                    } else if (x==1 && y==2) {
                        if (fin[i][j]) {
                            int v=mod(mod(d,j,0),j+1,0);
                            if (!v) ans+=g[k];
                        }
                    } else if (x==2 && y==1) {
                        int v=mod(mod(d,j,0),j+1,0);
                        f[id[v]]+=g[k];
                    }
                }
            }
        }
    }
    return ans+f[id[0]];
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    int T;
    scanf("%d",&T);
    for (int t=1;t<=T;++t) printf("Case %d: %lld\n",t,work());
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/7477884.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值