联合省选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
posted @ 2023-04-24 15:46  jucason_xu  阅读(21)  评论(0编辑  收藏  举报