首页 > 其他 > 详细

BZOJ4572 [Scoi2016]围棋

时间:2019-03-14 20:26:10      阅读:203      评论:0      收藏:0      [点我收藏+]

题意

技术分享图片
F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser   autointLogout 捐赠本站
Problem 4572. -- [Scoi2016]围棋

4572: [Scoi2016]围棋

Time Limit: 50 Sec  Memory Limit: 512 MB
Submit: 252  Solved: 154
[Submit][Status][Discuss]

Description

近日,谷歌研发的围棋AI—AlphaGo以4:1的比分战胜了曾经的世界冠军李世石,这是人工智能领域的又一里程碑。
与传统的搜索式AI不同,AlphaGo使用了最近十分流行的卷积神经网络模型。在卷积神经网络模型中,棋盘上每一
块特定大小的区域都被当做一个窗口。例如棋盘的大小为5×6,窗口大小为2×4,那么棋盘中共有12个窗口。此外
,模型中预先设定了一些模板,模板的大小与窗口的大小是一样的。下图展现了一个5×6的棋盘和两个2×4的模板
。对于一个模板,只要棋盘中有某个窗口与其完全匹配,我们称这个模板是被激活的,否则称这个模板没有被激活
。例如图中第一个模板就是被激活的,而第二个模板就是没有被激活的。我们要研究的问题是:对于给定的模板,
有多少个棋盘可以激活它。为了简化问题,我们抛开所有围棋的基本规则,只考虑一个n×m的棋盘,每个位置只能
是黑子、白子或无子三种情况,换句话说,这样的棋盘共有3n×m种。此外,我们会给出q个2×c的模板。我们希望
知道,对于每个模板,有多少种棋盘可以激活它。强调:模板一定是两行的。

Input

输入数据的第一行包含四个正整数n,m,c和q,分别表示棋盘的行数、列数、模板的列数和模板的数量。随后2×q
行,每连续两行描述一个模板。其中,每行包含c个字符,字符一定是‘W’,‘B’或‘X’中的一个,表示白子、
黑子或无子三种情况的一种。N<=100,M<=12,C<=6,Q<=5

Output

 输出应包含q行,每行一个整数,表示符合要求的棋盘数量。由于答案可能很大,你只需要输出答案对1,000,000,007取模后的结果即可。

Sample Input

3 1 1 2
B
W
B
B

Sample Output

6
5

HINT

Source

[Submit][Status][Discuss]
?
HOME Back

无图差评,样例只有一组差评,数据范围没给全差评。
技术分享图片
技术分享图片

分析

考虑反面,用状压DP求出不合法的方案数,用\(3^{n*m}\)减去它就行了。

如果模板串只有一行,那么状态显然是\(f[i][j]\),表示长度为\(i\)匹配到状态\(j\)未出现模板串的方案数。

考虑多了一行怎么做。显然不能两行都匹配上了,但是只有一行匹配上是可行的。那么就要记录二进制状态\(S\)表示上一行每个位置作为开头是否完全匹配第一个串。为了方便转移\(S\),还要记录当前位置匹配到了第一个串的哪个位置。

那么设\(f[i][j][S][x][y]\)表示填到了\((i,j)\),上一行每个位置作为开头是否完全匹配第一个串的状态为\(S\),与第一个串kmp匹配到了\(x\),与第二个串kmp匹配到了\(y\)的方案数。然后直接加法转移即可。实现的时候不用记录状态\((i,j)\)

可以预处理出自动机,加速匹配。

时间复杂度\(O(nm2^{m-c+1}c^2)\),最后那个测试点是5e6.

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef long long ll;

co int mod=1e9+7;
int n,m,c,T,nxt[7],ta[6][3],tb[6][3],na,nb;
char a[8],b[8];
int id(char x) {return x=='B'?0:(x=='W'?1:2);}
int U,f[1024][6][6],g[1024][6][6],ans;
void up(int&x,int y) {x+=y;if(x>=mod)x-=mod;}
void clear() {for(int S=0;S<U;++S)for(int x=0;x<c;++x)for(int y=0;y<c;++y)g[S][x][y]=0;}
void copy() {for(int S=0;S<U;++S)for(int x=0;x<c;++x)for(int y=0;y<c;++y)f[S][x][y]=g[S][x][y];}
int main(){
//  freopen(".in","r",stdin),freopen(".out","w",stdout);
    read(n),read(m),read(c),read(T);
    while(T--){
        scanf("%s%s",a+1,b+1);
        for(int i=1;i<=c;++i)a[i]=id(a[i]),b[i]=id(b[i]);
        for(int j=nxt[1]=0,i=2;i<=c;nxt[i++]=j){
            while(j&&a[j+1]!=a[i]) j=nxt[j];
            if(a[j+1]==a[i]) ++j;
        }
        na=nxt[c];
        for(int i=0;i<c;++i)for(int j=0,k;j<3;++j){
            for(k=i;k&&a[k+1]!=j;k=nxt[k]);
            if(a[k+1]==j) ++k;
            ta[i][j]=k;
        }
        for(int j=nxt[1]=0,i=2;i<=c;nxt[i++]=j){
            while(j&&b[j+1]!=b[i]) j=nxt[j];
            if(b[j+1]==b[i]) ++j;
        }
        nb=nxt[c];
        for(int i=0;i<c;++i)for(int j=0,k;j<3;++j){
            for(k=i;k&&b[k+1]!=j;k=nxt[k]);
            if(b[k+1]==j) ++k;
            tb[i][j]=k;
        }
        U=1<<(m-c+1);
        for(int S=0;S<U;++S)for(int x=0;x<c;++x)for(int y=0;y<c;++y)f[S][x][y]=0;
        for(int i=f[0][0][0]=1;i<=n;++i){
            clear();
            for(int S=0;S<U;++S)for(int x=0;x<c;++x)for(int y=0;y<c;++y)
                if(f[S][x][y]) up(g[S][0][0],f[S][x][y]);
            copy();
            for(int j=1;j<=m;++j){
                clear();
                for(int S=0;S<U;++S)for(int x=0;x<c;++x)for(int y=0;y<c;++y)
                    if(f[S][x][y]) for(int k=0,A,B,E;k<3;++k){
                        E=S;
                        if(j>=c) if(S>>(j-c)&1) E^=1<<(j-c); // init
                        A=ta[x][k];
                        if(A==c) E|=1<<(j-c),A=na;
                        B=tb[y][k];
                        if(B==c){
                            if(S>>(j-c)&1) continue;
                            B=nb;
                        }
                        up(g[E][A][B],f[S][x][y]);
                    }
                copy();
            }
        }
        ans=1;
        for(int i=n*m;i;--i) ans=3LL*ans%mod;
        for(int S=0;S<U;++S)for(int x=0;x<c;++x)for(int y=0;y<c;++y) up(ans,mod-f[S][x][y]);
        printf("%d\n",ans);
    }
    return 0;
}

加强版

由于那个状态\(S\)跟棋子无关,所以可以增加棋子种类。另外可以规定某些位置不能填什么,某些位置必须填什么。

BZOJ4572 [Scoi2016]围棋

原文:https://www.cnblogs.com/autoint/p/10533089.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!