题解 P9169【[省选联考 2023] 过河卒】
十分考验代码实现能力。
题意
一个 的棋盘,有的位置有障碍物。有两个红子和一个黑子。对于一个红子 ,移动一次可以走到 或 ,对于一个黑子,移动一次可以走到 或 。需要保证移动后还在地图内且不在障碍物上。两个红子不能重叠。双方轮流执子,红方为先手,红方每次可挑一个棋子进行移动。
在一次移动前,按照以下顺序判定胜负:
- 黑子处在 ,黑方胜;
- 一个红子和黑子在同一位置上,本次移动方负;
- 本次移动方无棋子可移动,本次移动方负;
- 都不满足则由本次移动方执行本次移动。
假设双方均采用最优策略,在可获胜时希望总移动步数最少,否则希望总移动步数最多。如果游戏无法结束则判定平局。
组数据,给出地图,求获胜方、总移动步数或判定平局。
。
思路
对于三个棋子,分别记作 。求出三个棋子所在连通块的所有点,列出所有可能存在的局面(包含三个点的位置和先手,显然上限是 种),在局面内建边 表示 局面一次移动后可以转移到 局面。建反向边的原因是 会决策走到哪个局面更好,当 决策完了可以更好地更新 的决策。
考虑求出所有点最优方案,考虑一个 bfs 和拓扑排序的融合东西,维护一个队列,存当前决策完了的所有点。初始时队列里的点是所有的结束结点(在这个局面移动之前已经判定胜负)。维护每个点获胜方和结束步数 。
考虑一个点 去更新点 :
- 如果 目前没有使得 先手胜利的方案:
- 如果 中获胜的是 的后手:,因为是 bfs,所以此时 肯定不小于 ;
- 否则:,并标记 已经找到使得先手胜利的方案;
- 否则:由于是 bfs,即使 中获胜的是 的先手,此时的 也更新不了 。
所以当一个点被决策完毕当且仅当满足两条件之一:
- 入度为 ;
- 第一次找到使得先手胜利的方案。
满足两条件之一时将其加入队列即可。
如果初始局面的结点没有被决策完毕,则必定平局,否则根据获胜方判断即可。时间复杂度 。
注意些卡常手段:
vector
开 比较难接受,建议使用链式前向星。- 不要使用
map
使复杂度退化,局面最多只有 ,可以直接存。 - 要求 横坐标比 横坐标小可以减少状态数。
代码:
#include<bits/stdc++.h>
using namespace std;
//read(),getc()
const int N=10+2,M=1e6+10,INF=1e9;
int id,T,n,m,cnt;
bool ok[N][N],vis[N][N];
struct Point{
int x,y;
Point(){x=y=0;}
Point(int a,int b){x=a,y=b;}
inline friend bool operator==(const Point &a,const Point &b){
return a.x==b.x&&a.y==b.y;
}
inline friend bool operator<(const Point &a,const Point &b){
return a.x<b.x||a.x==b.x&&a.y<b.y;
}
}stk[3][N*N];
int top[3];
inline bool checkR(int x,int y){
return ok[x-1][y]|ok[x][y+1]|ok[x][y-1]|ok[x+1][y];
}
inline bool checkB(int x,int y){
return ok[x-1][y]|ok[x][y+1]|ok[x][y-1];
}
inline bool checkR(Point tmp1,Point tmp2){
ok[tmp1.x][tmp1.y]=ok[tmp2.x][tmp2.y]=0;
bool tmp=checkR(tmp1.x,tmp1.y)||checkR(tmp2.x,tmp2.y);
ok[tmp1.x][tmp1.y]=ok[tmp2.x][tmp2.y]=1;
return tmp;
}
inline bool checkB(Point tmp){
return checkB(tmp.x,tmp.y);
}
int DX[]={1,0,0,-1},DY[]={0,-1,1,0};
inline void bfs(Point st,int ty){
for(int i=0;i<=n+1;i++)
for(int j=0;j<=m+1;j++)
vis[i][j]=!ok[i][j];
queue<Point>q;
q.push(st);
vis[st.x][st.y]=1;
top[ty]=0;
while(!q.empty()){
Point tmp=q.front();
stk[ty][++top[ty]]=tmp;
q.pop();
int px=tmp.x,py=tmp.y;
for(int i=!ty;i<=3;i++){
int nx=px+DX[i],ny=py+DY[i];
if(vis[nx][ny]) continue;
vis[nx][ny]=1;
q.push(Point(nx,ny));
}
}
}
int fr[M*2];
struct Edge{
int to,nxt;
}a[M*14];
int head[M*2],ent;
int deg[M*2];
short edn[M*2],stp[M*2];
bool vi2[M*2],fin[M*2],vi3[M*2];
int ind[2][M*2];
inline void upd(short &x,int y){
if(!x) x=y;
}
inline void add(int u,int v){
if(!u||!v) return ;
ent++;
a[ent].to=u;
a[ent].nxt=head[v];
head[v]=ent;
deg[u]++;
}
inline int cng(int a,int b,int c,int d,int e,int f){
return f+11*(e+11*(d+11*(c+11*(b+11*a))));
}
inline int get(Point a,Point b,Point c,int fr){
if(b<a) swap(a,b);
return ind[fr-1][cng(a.x,a.y,b.x,b.y,c.x,c.y)];
}
inline void upd(Point a,Point b,Point c,int fr,int cnt){
if(b<a) swap(a,b);
ind[fr-1][cng(a.x,a.y,b.x,b.y,c.x,c.y)]=cnt;
}
inline int get2(Point a,Point b,Point c){
return vi3[cng(a.x,a.y,b.x,b.y,c.x,c.y)];
}
inline void upd2(Point a,Point b,Point c,int cnt){
vi3[cng(a.x,a.y,b.x,b.y,c.x,c.y)]=cnt;
}
int main(){
// freopen("P9169_14.in","r",stdin);
// freopen("zu.out","w",stdout);
id=read(),T=read();
while(T--){
n=read(),m=read();
for(int i=0;i<=n+1;i++)
for(int j=0;j<=m+1;j++)
ok[i][j]=0;
Point r1,r2,b;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
char ch=getc();
if(ch!='#') ok[i][j]=1;
if(ch=='X') b.x=i,b.y=j;
if(ch=='O'){
Point tmp(i,j);
if(r1.x) r2=tmp;
else r1=tmp;
}
}
bfs(r1,1),bfs(r2,2),bfs(b,0);
cnt=0;
for(int i=1;i<=top[1];i++)
for(int j=1;j<=top[2];j++){
Point R1=stk[1][i],R2=stk[2][j];
if(R1==R2) continue;
if(R2<R1) swap(R1,R2);
for(int k=1;k<=top[0];k++){
Point B=stk[0][k];
if(get(R1,R2,B,1)) continue;
// Statu tmp0(R1,R2,B,1),tmp1(R1,R2,B,2);
fr[++cnt]=1,upd(R1,R2,B,1,cnt),upd2(R1,R2,B,0);
fr[++cnt]=2,upd(R1,R2,B,2,cnt);
edn[cnt]=edn[cnt-1]=deg[cnt]=deg[cnt-1]=vi2[cnt-1]=vi2[cnt]=fin[cnt-1]=fin[cnt]=0;
if(B.x==1) upd(edn[cnt-1],2),upd(edn[cnt],2),fin[cnt-1]=fin[cnt]=1;
if(R1==B||R2==B) upd(edn[cnt-1],2),upd(edn[cnt],1),fin[cnt-1]=fin[cnt]=1;
if(!checkR(R1,R2)) upd(edn[cnt-1],2),fin[cnt]=1;
if(!checkB(B)) upd(edn[cnt],1),fin[cnt]=1;
upd(edn[cnt-1],2),upd(edn[cnt],1);
}
}
ent=0;
for(int i=1;i<=cnt;i++)
head[i]=0;
int i=0;
for(int i0=1;i0<=top[1];i0++)
for(int j=1;j<=top[2];j++){
Point r1=stk[1][i0],r2=stk[2][j];
if(r1==r2) continue;
if(r2<r1) swap(r1,r2);
for(int k=1;k<=top[0];k++){
Point b=stk[0][k];
if(get2(r1,r2,b)) continue;
upd2(r1,r2,b,1);
i++;
if(!fin[i]){
int px=r1.x,py=r1.y;
for(int j=0;j<4;j++){
int nx=px+DX[j],ny=py+DY[j];
add(i,get(Point(nx,ny),r2,b,2));
}
px=r2.x,py=r2.y;
for(int j=0;j<4;j++){
int nx=px+DX[j],ny=py+DY[j];
add(i,get(r1,Point(nx,ny),b,2));
}
}
i++;
if(!fin[i]){
int px=b.x,py=b.y;
for(int j=1;j<4;j++){
int nx=px+DX[j],ny=py+DY[j];
add(i,get(r1,r2,Point(nx,ny),1));
}
}
}
}
memset(ind,0,sizeof(ind));
queue<int>q;
for(int i=1;i<=cnt;i++)
stp[i]=0,vi2[i]=0;
for(int i=1;i<=cnt;i++)
if(!deg[i]) q.push(i),vi2[i]=1;
while(!q.empty()&&!vi2[1]){
int P=q.front();q.pop();
short x=stp[P]+1;
for(int i=head[P];i;i=a[i].nxt){
int tmp=a[i].to;
if(vi2[tmp]) continue;
if(fr[tmp]==2){
if(edn[tmp]==1){
if(edn[P]!=2) stp[tmp]=x;
else{
stp[tmp]=x,edn[tmp]=2;
if(!vi2[tmp]) vi2[tmp]=1,q.push(tmp);
}
}
}else{
if(edn[tmp]==2){
if(edn[P]!=1) stp[tmp]=x;
else{
stp[tmp]=x,edn[tmp]=1;
if(!vi2[tmp]) vi2[tmp]=1,q.push(tmp);
}
}
}
deg[tmp]--;
if(deg[tmp]==0&&!vi2[tmp]) vi2[tmp]=1,q.push(tmp);
}
}
if(vi2[1]){
if(edn[1]==1) printf("Red %d\n",stp[1]);
else printf("Black %d\n",stp[1]);
}else puts("Tie");
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端