联合省选2023 D2T1 过河卒
我们可以先 \(dp\),设 \(f_{i,j,k,l}\) 和 \(g_{i,j,k,l}\)表示当前三个棋子分别在点 \(i,j,k\),目前轮到 \(l\) 走,谁胜利,最终会走多少步。
然后我们发现,变成一个有向图博弈。并且 \(l\) 是由 \(i,j,k\) 的奇偶性唯一确定的。就可以在图上直接做了。
首先我们发现,我们其实可以把初始的六个坐标编码成三个 \(i,j,k\),再编码成唯一的一个数对应图上的一个节点。并且最好不要解码出来。因为乘法编码解码的时候要取模,位运算编码占用多余空间大。
然后在可以转移到的状态之间连有向边。链式前向星存图有优势。
我们可以先处理出所有的终止状态:不同颜色点重合的、黑棋子到达终点的、某一方不能动的(也就是度数为 \(0\) 的)。
接下来,我们从所有的终止节点开始倒着 bfs。
一种做法是两次 bfs,在 bfs 的过程中,如果遇到了一定会的转移就转移上去,(例如红一定转移到红胜利的转移),而记录每个点的出度,除非所有能转移到的状态胜利方都和自己不一样,否则不会去转移。
然后再在有结果的方案中再 bfs 一遍,得到最终的步数。
但是我们发现这个其实可以一起做,因为两次都是 bfs,加入方法的判定是一致的。(因为 bfs 得到最终步数最优的证明是在第二种转移中,最后一个有贡献的转移一定是所有合法转移中步数最多的一个,符合败方的需求)我们就可以得到比较优美的做法。
贴一下个人觉得最好的 bfs 片段(by songjiaxing)
const int N=15,T=2000005;
int n,m,a[N][N],ql,qr,cnt;
int O[2],X,nv,dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int mv[T],f[T],g[T],q[T],ava[T],deg[T];
vt<int>vv[T],ve[T];
st s;
inline void init(){
ql=1,qr=0,nv=0;
O[0]=O[1]=X=0;
rep(i,0,N-1)rep(j,0,N-1)a[i][j]=0;
rep(i,0,T-1)f[i]=-1,g[i]=0,ava[i]=0,deg[i]=0;
rep(i,0,T-1)vv[i].clear(),ve[i].clear();
}
inline void input(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s;
for(int j=1;j<=m;j++){
if(s[j-1]!='#')a[i][j]=1;
if(s[j-1]=='X')X=(i-1)*m+j,nv^=(i&1)^(j&1);
if(s[j-1]=='O')(O[0]?O[1]=(i-1)*m+j:O[0]=(i-1)*m+j),nv^=(i&1)^(j&1);
}
}
}
inline int code(int *x,int *y){
int res=0,fl=0;
if(x[1]==x[2]&&y[1]==y[2])return -1;
if((x[1]-1)*m+y[1]>(x[2]-1)*m+y[2]){
fl=1;swap(x[1],x[2]);swap(y[1],y[2]);
}
rd(i,3)res<<=7,res|=((x[i]-1)*m+y[i]);
if(fl)swap(x[1],x[2]),swap(y[1],y[2]);
return res;
}
inline void solve(){
init();
input();
int x[3],y[3];
int test=((((6-1)*m+4)<<14)|(((4-1)*m+5)<<7)|(((9-1)*m+3)));
FOR(x[0],1,n)FOR(y[0],1,m)if(a[x[0]][y[0]]){
FOR(x[1],1,n)FOR(y[1],1,m)if(a[x[1]][y[1]]){
FOR(x[2],x[1],n)FOR(y[2],1,m)if(a[x[2]][y[2]]){
if(x[1]==x[2]&&y[1]>=y[2])continue;
int cur=code(x,y);mv[cur]=nv,ava[cur]=1;
rd(l,3)mv[cur]^=(x[l]&1)^(y[l]&1);
if(x[0]==1){
f[cur]=1,g[cur]=0,q[++qr]=cur;
continue;
}
rd(l,3)if((l!=0)==(mv[cur]^1))rd(dir,4)if(l||dir){
int nx=x[l]+dx[dir],ny=y[l]+dy[dir],rx=x[l],ry=y[l];
if(!a[nx][ny])continue;
x[l]=nx,y[l]=ny;
int cyr=code(x,y);
if(cyr!=-1)vv[cur].pb(cyr),ve[cyr].pb(cur),deg[cur]++;
x[l]=rx,y[l]=ry;
}if(!deg[cur])f[cur]=mv[cur]^1,g[cur]=0,q[++qr]=cur;
if((x[0]-1)*m+y[0]==(x[1]-1)*m+y[1]||(x[0]-1)*m+y[0]==(x[2]-1)*m+y[2]){
f[cur]=mv[cur]^1,g[cur]=0,q[++qr]=cur;
}
}
}
}int goal=(X<<14)|(O[0]<<7)|(O[1]);
while(ql<=qr){
int cur=q[ql++];
if(cur==goal){
if(f[cur]==1)cout<<"Black "<<g[cur]<<endl;
else cout<<"Red "<<g[cur]<<endl;
return;
}
for(auto cyr:ve[cur])if(f[cyr]==-1){
if(mv[cyr]==f[cur]){
f[cyr]=mv[cyr],g[cyr]=g[cur]+1,q[++qr]=cyr;
}else{
deg[cyr]--;
if(!deg[cyr])f[cyr]=mv[cur],g[cyr]=g[cur]+1,q[++qr]=cyr;
}
}
}cout<<"Tie"<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int tid,t;
cin>>tid>>t;
rd(_,t)solve();
return 0;
}
//Crayan_r