4572: [Scoi2016]围棋

4 篇文章 0 订阅

4572: [Scoi2016]围棋

Time Limit: 50 Sec   Memory Limit: 512 MB
Submit: 108   Solved: 60
[ 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]



考虑轮廓线dp,,(第一次写。。。。。)一开始设计状态老半天都找不到合适的。。。

正难则反,用总方案数 - 没出现匹配的方案数

定义f[i][j][s][x][y]:以(i,j)结束的轮廓线,每个位置是否与第一个串匹配(数码s),结束位置与第一个串匹配到第x位,与第二个串匹配到第y位,方案数

能想到的话就能实现了。。先做个KMP,转移不难

可以考虑像BFS一样转移,因为不可能所有状态都出现过。。。

但,,常数还是非常爆炸的O(n*m*2^m*c^2)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = (1<<12);
typedef long long LL;
const LL mo = 1000000007;
#define FOR(a,b,c) for (int a = b; a <= c; a++)
 
struct data{
    int s,x,y; data(){}
    data(int s,int x,int y): s(s),x(x),y(y){}
};
 
int n,m,c,q,G,g,s0[8],s1[8],f0[7],f1[7];
LL f[2][maxn][7][7];
char ch[7]; bool vis[2][maxn][7][7];
 
queue <data> Q[2];
 
void KMP_pre(int *s,int *F)
{
    scanf("%s",ch + 1);
    FOR(i,1,c)
    {
        if (ch[i] == 'W') s[i] = 0;
        else if (ch[i] == 'B') s[i] = 1;
        else s[i] = 2;
    }
    F[1] = 0; s0[c+1] = s1[c+1] = -1;
    FOR(i,1,c-1)
    {
        int v = F[i];
        while (v && s[v+1] != s[i+1]) v = F[v];
        F[i+1] = s[v+1] == s[i+1]?v+1:v;
    }
}
 
void Work(int now,int nex,int j)
{
    while (!Q[now].empty())
    {
        data K = Q[now].front(); Q[now].pop();
        int s = K.s,x = K.x,y = K.y;
        f[now][s][x][y] %= mo;
        if (!f[now][s][x][y]) continue;
        int S = s<<1; if (S >= g) S -= g;
        FOR(k,0,2)
        {
            int X,Y; if (j == m) X = Y = 0; else X = x,Y = y;
            while (X && s0[X+1] != k) X = f0[X];
            X = s0[X+1] == k?X+1:X;
            while (Y && s1[Y+1] != k) Y = f1[Y];
            Y = s1[Y+1] == k?Y+1:Y;
            int ns = S | ((X == c)?1:0);
            if ((s>>m-1) && Y == c) continue;
            f[nex][ns][X][Y] += f[now][s][x][y];
            if (!vis[nex][ns][X][Y]) vis[nex][ns][X][Y] = 1,Q[nex].push(data(ns,X,Y));
        }   
    }
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    cin >> n >> m >> c >> q; G = (1<<m)-1; g = (1<<m);
    while (q--)
    {
        KMP_pre(s0,f0); KMP_pre(s1,f1); while (!Q[1].empty()) Q[1].pop();
        memset(f[0],0,sizeof(f[0])); while (!Q[0].empty()) Q[0].pop();
        f[0][0][0][0] = 1; int now = 0,nex = 1; Q[0].push(data(0,0,0));
        FOR(i,0,n) FOR(j,1,m)
        {
            if (!i && j != m) continue;
            if (i == n && j == m) break;
            memset(f[nex],0,sizeof(f[nex]));
            memset(vis[nex],0,sizeof(vis[nex]));
            Work(now,nex,j); swap(now,nex);
        } 
        LL tot = 0;
        FOR(s,0,G) FOR(x,0,c) FOR(y,0,c) tot += f[now][s][x][y];
        tot %= mo;
        LL Ans = 1; FOR(i,1,n) FOR(j,1,m) Ans *= 3LL,Ans %= mo;
        Ans = (Ans - tot + mo) % mo; printf("%d\n",(int)(Ans));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值