#博弈论,拓扑排序#洛谷 9169 [省选联考 2023] 过河卒
分析
状态是三个点,两个红点可以用行优先压缩,并且只有与起点状态奇偶性相同的才会被起点经过。
所以一共就 \(nm\binom{nm}{2}\) 个状态,建反图先手必胜当且仅当后手有一个后继状态必败;
先手必败当且仅当后手所有后继状态必胜(这里需要拓扑排序),同样拓扑排序就能让步数尽量大。
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N=500011,M=11,dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; queue<int>q; char s[M][M]; vector<int>G[N];
int n,m,tot,blackx,blacky,red0x,red0y,red1x,red1y,sum,S,rk[M][M][M][M][M][M],draw[N],Odd[N],deg[N],dis[N],win[N];
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
bool block(int i1,int j1,int i2,int j2,int i3,int j3){
if (i1<1||i2<1||i3<1||j1<1||j2<1||j3<1) return 1;
if (i1>n||i2>n||i3>n||j1>m||j2>m||j3>m) return 1;
if (s[i1][j1]=='#'||s[i2][j2]=='#'||s[i3][j3]=='#'||(i2==i3&&j2==j3)) return 1;
return 0;
}
int Abs(int x){return x<0?-x:x;}
bool odd(int i1,int j1,int i2,int j2,int i3,int j3){return Abs(sum-i1-j1-i2-j2-i3-j3)&1;}
int main(){
iut();
for (int T=iut();T;--T){
n=iut(),m=iut(),tot=0;
for (int i=1;i<=n;++i){
char ch=getchar();
for (int j=1;j<=m;++j){
while (ch!='O'&&ch!='X'&&ch!='#'&&ch!='.') ch=getchar();
s[i][j]=ch,ch=getchar();
}
for (int j=1;j<=m;++j)
if (s[i][j]=='O') red0x=red1x,red0y=red1y,red1x=i,red1y=j;
else if (s[i][j]=='X') blackx=i,blacky=j;
}
sum=blackx+blacky+red0x+red0y+red1x+red1y;
for (int i1=1;i1<=n;++i1)
for (int j1=1;j1<=m;++j1)
for (int i2=1;i2<=n;++i2)
for (int j2=1;j2<=m;++j2)
for (int i3=i2;i3<=n;++i3)
for (int j3=(i2==i3?(j2+1):1);j3<=m;++j3)
if (!block(i1,j1,i2,j2,i3,j3))
rk[i1][j1][i2][j2][i3][j3]=++tot;
S=rk[blackx][blacky][red0x][red0y][red1x][red1y];
for (int i=1;i<=tot;++i) draw[i]=1;
for (int i1=1;i1<=n;++i1)
for (int j1=1;j1<=m;++j1)
for (int i2=1;i2<=n;++i2)
for (int j2=1;j2<=m;++j2)
for (int i3=i2;i3<=n;++i3)
for (int j3=(i2==i3?(j2+1):1);j3<=m;++j3)
if (!block(i1,j1,i2,j2,i3,j3)){
int now=rk[i1][j1][i2][j2][i3][j3];
if (odd(i1,j1,i2,j2,i3,j3)){
Odd[now]=1;
for (int k=0;k<4;++k){
int I2=i2+dx[k],J2=j2+dy[k];
if (!block(i1,j1,I2,J2,i3,j3)){
if (I2>i3||(I2==i3&&J2>j3)) G[now].push_back(rk[i1][j1][i3][j3][I2][J2]);
else G[now].push_back(rk[i1][j1][I2][J2][i3][j3]);
}
}
for (int k=0;k<4;++k){
int I3=i3+dx[k],J3=j3+dy[k];
if (!block(i1,j1,i2,j2,I3,J3)){
if (i2>I3||(i2==I3&&j2>J3)) G[now].push_back(rk[i1][j1][I3][J3][i2][j2]);
else G[now].push_back(rk[i1][j1][i2][j2][I3][J3]);
}
}
}else{
Odd[now]=0;
for (int k=0;k<3;++k){
int I1=i1+dx[k],J1=j1+dy[k];
if (!block(I1,J1,i2,j2,i3,j3))
G[now].push_back(rk[I1][J1][i2][j2][i3][j3]);
}
}
}
for (int i1=1;i1<=n;++i1)
for (int j1=1;j1<=m;++j1)
for (int i2=1;i2<=n;++i2)
for (int j2=1;j2<=m;++j2)
for (int i3=i2;i3<=n;++i3)
for (int j3=(i2==i3?(j2+1):1);j3<=m;++j3)
if (!block(i1,j1,i2,j2,i3,j3)){
int now=rk[i1][j1][i2][j2][i3][j3];
sort(G[now].begin(),G[now].end());
G[now].erase(unique(G[now].begin(),G[now].end()),G[now].end());
for (int y:G[now]) ++deg[y];
if (i1==1) draw[now]=dis[now]=0,win[now]=Odd[now];
else if ((i1==i2&&j1==j2)||(i1==i3&&j1==j3)) draw[now]=dis[now]=0,win[now]=0;
}
while (!q.empty()) q.pop();
for (int i=1;i<=tot;++i){
if (draw[i]&&!deg[i]) draw[i]=dis[i]=0,win[i]=0;
if (!draw[i]) q.push(i);
}
while (!q.empty()){
int x=q.front(); q.pop();
if (x==S) break;
if (!win[x]){
for (int y:G[x])
if (draw[y]){
win[y]=1,draw[y]=0,--deg[y];
dis[y]=dis[x]+1,q.push(y);
}
}else{
for (int y:G[x])
if (draw[y]&&--deg[y]==0){
win[y]=0,draw[y]=0;
dis[y]=dis[x]+1,q.push(y);
}
}
}
if (draw[S]) printf("Tie\n");
else if (win[S]) printf("Red %d\n",dis[S]);
else printf("Black %d\n",dis[S]);
for (int i=1;i<=tot;++i) draw[i]=win[i]=dis[i]=deg[i]=Odd[i]=0;
for (int i=1;i<=tot;++i) G[i].erase(G[i].begin(),G[i].end());
}
return 0;
}

浙公网安备 33010602011771号