直接博弈论dfs可以得到75分(然鹅我博弈论学的很pie)

#include<bits/stdc++.h>
#define LL long long
#define INF 10000
using namespace std;
int read()
{
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
char s[45];
int lr[]={0,0,1,-1},ud[]={1,-1,0,0},ans[1003],tu[43][43],sg[43];
int X,Y,tot=0,n,m;
int dfs(int x,int y,int op)
{
for(int i=0;i<4;++i)
{
int xx=x+ud[i],yy=y+lr[i];
if(xx<1||xx>n||yy<1||yy>m||tu[xx][yy]!=op)continue;
swap(tu[xx][yy],tu[x][y]);
if(!dfs(xx,yy,op^1)){swap(tu[xx][yy],tu[x][y]);return 1;}//往下递归得到每一个局面,判断其为必胜还是必败
swap(tu[xx][yy],tu[x][y]);
}
return 0;
}
int main()
{
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;++i)
{
scanf("%s",s+1);
for(int j=1;j<=m;++j)
{
if(s[j]=='X')tu[i][j]=1;//黑棋
else if(s[j]=='.')tu[i][j]=2,X=i,Y=j;//空格
}
}
int op=read();
for(int i=1;i<=op;++i)
{
int x=read(),y=read();
sg[i]=dfs(X,Y,0);
swap(tu[x][y],tu[X][Y]);
X=x;Y=y;
if(sg[i]&&dfs(X,Y,1))ans[++tot]=i;//走之前兔兔是必胜态,走之后蛋蛋是必胜态
x=read(),y=read();
swap(tu[x][y],tu[X][Y]);
X=x;Y=y;
}
printf("%d\n",tot);
for(int i=1;i<=tot;++i)
printf("%d\n",ans[i]);
}
/*
*/
然后正解是二分图。。。哇这谁想得到是二分图
让我们认真分析一下:
每一次顺序移动白格黑格,当最后无法移动停止,且每一个格子只会走过一次(感性感知)相当于是黑白交替格子染色—>想到二分图。
与其说是每次交换空格和其他黑白格,不如说是空格在黑白格上走。(第一步在空格本身上)
我们可以把第一步认为是黑格,然后交替走黑白格,如果最后走到黑格,说明兔兔输了(兔兔移动白格)。
然后二分图匹配保证了最后走到的一定是白格,因为从黑格出发(要求每一条路都是奇数步)
走格子相当于是在增广路上走,如果当前x,y一定在最大匹配上(在增广路上而且没有其他的增广路可以替代它了),说明现在是必胜态。
然后对于每一步进行判断,如果兔兔和蛋蛋的第i步都是必胜态,说明兔兔在走第i步之前是必胜态,走了第i步后蛋蛋就是必胜态了。
满足题目要求,累计答案。

#include<bits/stdc++.h>
#define N 43
#define LL long long
#define INF 10000
using namespace std;
int read()
{
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
struct EDGE{
int nextt,to;
}w[N*N*4];
char s[N];
int lr[]={0,0,1,-1},ud[]={1,-1,0,0},ans[1003],tu[N][N],head[N*N],ord[N][N],vis[N*N],match[N*N],ban[N*N],win[1003];
int X,Y,tot=0,n,m,ndnum=0,res=0,timer=0;
void add(int a,int b)
{
tot++;
w[tot].nextt=head[a];
w[tot].to=b;
head[a]=tot;
}
void jian_tu()//建二分图
{
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(!tu[i][j])continue;
for(int k=0;k<4;++k)
{
int xx=i+ud[k],yy=j+lr[k];
if(xx<1||xx>n||yy<1||yy>m||tu[xx][yy])continue;
add(ord[i][j],ord[xx][yy]);
add(ord[xx][yy],ord[i][j]);
}
}
}
int dfs(int x)
{
if(vis[x]==timer||ban[x])return 0;
vis[x]=timer;
for(int i=head[x];i;i=w[i].nextt)
{
int v=w[i].to;
if(ban[v])continue;
if(!match[v]||dfs(match[v]))
{
match[v]=x;match[x]=v;
return 1;
}
}
return 0;
}
int main()
{
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;++i)
{
scanf("%s",s+1);
for(int j=1;j<=m;++j)
{
ord[i][j]=++ndnum;
if(s[j]=='X')tu[i][j]=1;//黑棋
else if(s[j]=='.')tu[i][j]=2,X=i,Y=j;//空格
}
}
jian_tu();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(tu[i][j])timer++,dfs(ord[i][j]);
int op=read();
for(int i=1;i<=op*2;++i)
{
int id=ord[X][Y];
ban[id]=1;
if(match[id])
{
int v=match[id];
match[id]=match[v]=0;
++timer;
win[i]=(!dfs(v));//在增广路上且不可代替
}
X=read();Y=read();
}
for(int i=1;i<=op;++i)
if(win[2*i-1]&&win[2*i])ans[++res]=i;
printf("%d\n",res);
for(int i=1;i<=res;++i)
printf("%d\n",ans[i]);
}
