4572: [Scoi2016]围棋
Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 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
B
W
B
B
Sample Output
6
5
5
HINT
Source
考虑轮廓线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;
}