题解 P9169【[省选联考 2023] 过河卒】

Link

十分考验代码实现能力。

题意

一个 n×m 的棋盘,有的位置有障碍物。有两个红子和一个黑子。对于一个红子 (x,y),移动一次可以走到 (x±1,y)(x,y±1),对于一个黑子,移动一次可以走到 (x1,y)(x,y±1)。需要保证移动后还在地图内且不在障碍物上。两个红子不能重叠。双方轮流执子,红方为先手,红方每次可挑一个棋子进行移动。

在一次移动前,按照以下顺序判定胜负:

  • 黑子处在 x=1,黑方胜;
  • 一个红子和黑子在同一位置上,本次移动方负;
  • 本次移动方无棋子可移动,本次移动方负;
  • 都不满足则由本次移动方执行本次移动。

假设双方均采用最优策略,在可获胜时希望总移动步数最少,否则希望总移动步数最多。如果游戏无法结束则判定平局。

T 组数据,给出地图,求获胜方、总移动步数或判定平局。

1T,n,m10

思路

对于三个棋子,分别记作 r1,r2,b。求出三个棋子所在连通块的所有点,列出所有可能存在的局面(包含三个点的位置和先手,显然上限是 O(n3m3) 种),在局面内建边 ts 表示 s 局面一次移动后可以转移到 t 局面。建反向边的原因是 s 会决策走到哪个局面更好,当 t 决策完了可以更好地更新 s 的决策。

考虑求出所有点最优方案,考虑一个 bfs 和拓扑排序的融合东西,维护一个队列,存当前决策完了的所有点。初始时队列里的点是所有的结束结点(在这个局面移动之前已经判定胜负)。维护每个点获胜方和结束步数 pi

考虑一个点 t 去更新点 s

  • 如果 s 目前没有使得 s 先手胜利的方案:
    • 如果 t 中获胜的是 s 的后手:pspt+1,因为是 bfs,所以此时 pt+1 肯定不小于 ps
    • 否则:pspt+1,并标记 s 已经找到使得先手胜利的方案;
  • 否则:由于是 bfs,即使 t 中获胜的是 s 的先手,此时的 pt+1 也更新不了 ps

所以当一个点被决策完毕当且仅当满足两条件之一:

  • 入度为 0
  • 第一次找到使得先手胜利的方案。

满足两条件之一时将其加入队列即可。

如果初始局面的结点没有被决策完毕,则必定平局,否则根据获胜方判断即可。时间复杂度 O(Tn3m3)

注意些卡常手段:

  • vector2×106 比较难接受,建议使用链式前向星。
  • 不要使用 map 使复杂度退化,局面最多只有 2×106,可以直接存。
  • 要求 r1 横坐标比 r2 横坐标小可以减少状态数。

代码:

#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");
    }
}
posted @   ffffyc  阅读(20)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示