【博弈论】游戏-纪中集训
考场上面就过了,但还是想说一下。
一看到Alice和Bob玩游戏就知道是博弈
刚开始还是想暴力
但是关于博弈的知识只记得是Nim游戏可以用异或和,还有就是sg函数。
看了一下异或和应该不可做,所以就开始算sg。
每个状态的子状态都只有2个:删去最后一行,或者是最后一列
要判断每一行,每一列的和是否是偶数,用前缀和处理
(这里据说可以用异或运算,但是我还没有搞出来,还据说可以压成一维的空间,也没有搞出来)
我感觉自己用的是假的sg函数,但sg函数的具体值好像没什么意义 用0表示必败态,用1表示非必败态(必胜态)
如果当前状态可以转移到必败态,那它就是必胜态,否则就是必败态。
刚开始写得很乱,后来整合了一下,压到了一个双重循环里面。想思路的时候,还是分开想比较好,码的时候,整合一下要清爽一些,(或许会)快一些。
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 1005
#define INF 0x3f3f3f3f
int a,s1[MAXN][MAXN],s2[MAXN][MAXN],sg[MAXN][MAXN];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,ans;
scanf("%d",&n);
sg[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&a);
a%=2;
s1[i][j]=(s1[i][j-1]+a)%2;
s2[i][j]=(s2[i-1][j]+a)%2;
sg[i][j]=0;
if(s1[i][j]==0)
{
if(sg[i-1][j]==0)
sg[i][j]=1;
}
if(s2[i][j]==0)
{
if(sg[i][j-1]==0)
sg[i][j]=1;
}
}
if(sg[n][n]==0) printf("L\n");
else printf("W\n");
}
return 0;
}
嗯,我把异或弄出来了!
其实比较简单吧
因为:
1
⊕
1
=
0
1⊕1=0
1⊕1=0
1
⊕
0
=
1
1⊕0=1
1⊕0=1
0
⊕
1
=
1
0⊕1=1
0⊕1=1
0
⊕
0
=
0
0⊕0=0
0⊕0=0
恰好对应模2的余数运算
所以:
s1[i][j]=(s1[i][j-1]+a)%2;
s2[i][j]=(s2[i-1][j]+a)%2;
↓
s1[i][j]=s1[i][j-1]^a;
s2[i][j]=s2[i-1][j]^a;
快了100多ms 还行
(其实我感觉压了空间也没什么用啊 1000的数据 我就3个二维数组 能过就行 )
我还以为是什么多高级的操作 ,原来就是把i-1那里抹掉不算,毕竟在算出sg函数之后本身的前缀和数组没有任何用处(而且这个前缀和数组只需要反应和的奇偶性)
s1表示第i行的前缀和,s2表示第j行的前缀和
于是:
s1[i][j]=s1[i][j-1]^a;
s2[i][j]=s2[i-1][j]^a;
↓
s1[i]=s1[i]^a;
s2[j]=s2[j]^a;
嗯?我WA了?原来是没有清0 (最短路逃
for(int i=1;i<=n;i++)
s1[i]=s2[i]=0;
memset要谨慎使用
嗯?怎么又快了200多ms? 687ms->481ms