题目链接:戳我
推荐题解
根据博弈论的基础知识,暴力搜索还是能写70分的qwq
蒟蒻写个暴力都写不好,别人能拿75,我就不知道这5分到底是怎么丢的了qwqwq
先放上我的70分代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
int n,m,k,xx,yy;
int move_x[5]={0,0,1,-1},move_y[5]={1,-1,0,0};
int pre[50][50],cur[50][50];
bool flag1,flag2;
//flag1表示兔兔是否有必胜策略,flag2表示蛋蛋是否有必胜策略
//o---1 x---2
char s[50];
vector<int>v;
inline int around(int x,int y)
{
bool iso=false,isx=false;
for(int i=0;i<4;i++)
{
int now_x=x+move_x[i];
int now_y=y+move_y[i];
if(now_x<1||now_x>n||now_y<1||now_y>m) continue;
if(cur[now_x][now_y]==1) iso=true;
if(cur[now_x][now_y]==2) isx=true;
}
if(iso==true&&isx==false) {//puts("1");
return 1;}
if(iso==false&&isx==true) {//puts("2");
return 2;}
//puts("3");
return 3;
}
inline void print1()
{
puts("cur---------------");
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
printf("%d",cur[i][j]);
cout<<endl;
}
puts("cur---------------");
}
inline void print2()
{
puts("pre---------------");
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
printf("%d",pre[i][j]);
cout<<endl;
}
puts("pre---------------");
}
inline bool check(int x,int y,int op,int name)//op(1)='O',op(2)='X'
{
//printf("x=%d y=%d op=%d name=%d
",x,y,op,name);
//print1();
if(op==1&&name==2&&around(x,y)==1) {//puts("yes");
return true;}
if(op==2&&name==1&&around(x,y)==2) {//puts("yes");
return true;}
if(op==1&&name==1&&around(x,y)==2) {//puts("no");
return false;}
if(op==2&&name==2&&around(x,y)==1) {//puts("no");
return false;}
bool flag;
if(op!=name) flag=true;
else flag=false;
for(int i=0;i<4;i++)
{
int now_x=x+move_x[i];
int now_y=y+move_y[i];
if(now_x<1||now_x>n||now_y<1||now_y>m||cur[now_x][now_y]!=name) continue;
//printf("cur[%d][%d]=%d
",now_x,now_y,cur[now_x][now_y]);
swap(cur[x][y],cur[now_x][now_y]);
if(op==name&&check(now_x,now_y,op,3-name)==true) flag=true;
if(op!=name&&check(now_x,now_y,3-op,3-name)==true) flag=false;
swap(cur[x][y],cur[now_x][now_y]);
}
//if(flag==true) cout<<"yes"<<endl;
//else cout<<"no"<<endl;
return flag;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
//printf("n=%d m=%d
",n,m);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
for(int k=0,len=strlen(s);k<len;k++)
{
pre[i][k+1]=(s[k]=='O'?1:2);
if(s[k]=='.') xx=i,yy=k+1,pre[i][k+1]=0;
}
}
//print2();
scanf("%d",&k);k<<=1;
memcpy(cur,pre,sizeof(pre));
if(check(xx,yy,1,1)) flag1=true,flag2=false;
else flag1=false,flag2=true;
//printf("flag1=%d flag2=%d
",flag1,flag2);
//cout<<endl<<endl;
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
//printf("i=%d
",i);
if(i&1)//兔兔移动
{
pre[xx][yy]=pre[x][y];
pre[x][y]=0;
xx=x,yy=y;
memcpy(cur,pre,sizeof(pre));
//print2();
bool flag=check(xx,yy,1,2);//表示移动之后兔兔还是否有必胜策略
//printf("rabbits flag=%d
",flag);
if(flag==false)
{
if(flag1==true) flag1=false,flag2=true,v.push_back(i+1>>1);
//printf("flag1=%d flag2=%d
",flag1,flag2);
}
else
{
if(flag1==false) flag1=true,flag2=false;
//printf("flag1=%d flag2=%d
",flag1,flag2);
}
}
else//蛋蛋移动
{
pre[xx][yy]=pre[x][y];
pre[x][y]=0;
xx=x,yy=y;
memcpy(cur,pre,sizeof(pre));
//print2();
bool flag=check(xx,yy,2,1);//表示移动之后蛋蛋是否有必胜策略
//printf("eggs flag=%d
",flag);
if(flag==false)
{
if(flag2==true) flag2=false,flag1=true;
//printf("flag1=%d flag2=%d
",flag1,flag2);
}
else
{
if(flag2==false) flag2=true,flag1=false;
//printf("flag1=%d flag2=%d
",flag1,flag2);
}
}
//cout<<endl<<endl;
}
printf("%d
",v.size());
for(int i=0;i<v.size();i++) printf("%d
",v[i]);
return 0;
}
update:其实我写的有点麻烦了,dfs传参数只用传当前操作的是谁,空格的位置即可。我多写了一个判断谁赢,所以特判就显得十分冗余了qwqwq
好了现在我们来考虑正解吧!
我们考虑游戏的过程——可以把它当做从空白格子开始走,走一条路径,黑白相间。而这很像匈牙利算法的增广路拓展——所以我们可以用二分图最大匹配来做这个题,如果一个点在二分图最大匹配上,是否一定被选中即可判定是否先手必胜qwq
5.7update:为什么在一个二分图最大匹配上就一定是先手必胜呢?因为二分图最大匹配的交错路径一定是只有奇数条,而先手显然可以一直沿着最大匹配走(即奇数次操作),所以到最后后手会无法操作,结论:先手必胜qwq。
正解:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<vector>
#define MAXN 50
using namespace std;
int n,m,nx,ny,t,tot,q;
int move_x[4]={0,0,1,-1},move_y[4]={1,-1,0,0};
int id[MAXN][MAXN],to[MAXN*MAXN],ans[MAXN*MAXN];
int head[MAXN*MAXN*2],nvis[MAXN*MAXN],done[MAXN*MAXN],pre[MAXN][MAXN];
char c[MAXN];
vector<int>ansans;
struct Edge{int nxt,to;}edge[MAXN*MAXN*2];
inline void add(int from,int to)
{
edge[++t].nxt=head[from],edge[t].to=to,head[from]=t;
edge[++t].nxt=head[to],edge[t].to=from,head[to]=t;
}
inline bool solve(int x)
{
if(nvis[x]==1) return false;
for(int i=head[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(done[v]==0&&nvis[v]==0)
{
done[v]=1;
if(to[v]==0||solve(to[v])==true)
{
to[v]=x,to[x]=v;
return true;
}
}
}
return false;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",c+1);
for(int j=1;j<=m;j++)
{
if(c[j]=='O') pre[i][j]=0;
else if(c[j]=='X') pre[i][j]=1;
else pre[i][j]=1,nx=i,ny=j;
}
}
scanf("%d",&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
id[i][j]=++tot;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(pre[i][j]==0) continue;
for(int k=0;k<=3;k++)
{
int now_x=i+move_x[k];
int now_y=j+move_y[k];
if(now_x<1||now_x>n||now_y<1||now_y>m||pre[now_x][now_y]) continue;
add(id[i][j],id[now_x][now_y]);
}
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(pre[i][j]==1)
{
memset(done,0,sizeof(done));
solve(id[i][j]);
}
for(int i=1;i<=q*2;i++)
{
int cur=id[nx][ny],now=to[cur];
nvis[cur]=1;
if(to[cur])
{
to[cur]=to[now]=0;
memset(done,0,sizeof(done));
ans[i]=!solve(now);
}
scanf("%d%d",&nx,&ny);
}
for(int i=1;i<=q;i++)
{
if(ans[i*2-1]==1&&ans[i*2]==1)
ansans.push_back(i);
}
printf("%d
",ansans.size());
for(int i=0;i<ansans.size();i++)
printf("%d
",ansans[i]);
return 0;
}