【BZOJ3106】[CQOI2013] 棋盘游戏(对抗搜索)

点此看题面

大致题意: 在一张\(n*n\)的棋盘上有一枚黑棋子和一枚白棋子。白棋子先移动,然后是黑棋子。白棋子每次可以向上下左右四个方向中任一方向移动一步,黑棋子每次则可以向上下左右四个方向中任一方向移动一至二步。当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。两个游戏者都很聪明,可以获胜时会尽快获胜,必输时会尽量拖延时间。试判断谁会赢,需要多少回合。

对抗搜索

这道题的做法应该是对抗搜索

一波简单的分析

我们先来对题目进行一波简单的分析。

不难发现,因为黑棋每次能走的步数大于白棋每次能走的步数,所以除非白棋第一步就吃掉黑棋,否则白棋必输

既然这样,我们只需特判白棋获胜的情况,然后题目就转换成了求黑棋追上白棋所需的时间

这样一来,就变成了一道较简单的对抗搜索题了。

直接用记忆化优化即可(当然,理论上来讲\(Alpha-Beta\)剪枝也可以做,但我没去试过)。

代码

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (putchar(ch))
#define N 20
int OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,OutputStack[Fsize];
using namespace std;
int n,X1,Y1,X2,Y2,res[N+1][N+1][N+1][N+1][2][3*N+1];
inline void read(int &x)
{
    x=0;static char ch;
    while(!isdigit(ch=tc()));
    while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
}
inline void write(int x)
{
    if(!x) return (void)pc('0');
    while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
    while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
}
inline int dfs(int X1,int Y1,int X2,int Y2,int Which,int Step)//对抗搜索
{
    if(Step>3*n) return 1e9;//深度限制,以防无限制地搜索下去(可以保证最终的答案≤3n)
    if(res[X1][Y1][X2][Y2][Which][Step]) return res[X1][Y1][X2][Y2][Which][Step];//如果已经访问当前状态,就返回上次求解出的答案
    if(X1==X2&&Y1==Y2) return Which?1e9:0;//如果已经重合了,就退出函数
    register int i,t,ans=Which?1e9:0;
    Which^=1,++Step;//更新Which和Step为下一个状态,以避免不断地运算
    if(Which)//如果下一个轮黑棋操作,即当前为白棋操作
    {
        if(X1>1) t=dfs(X1-1,Y1,X2,Y2,Which,Step),ans=max(ans,t);
        if(X1<n) t=dfs(X1+1,Y1,X2,Y2,Which,Step),ans=max(ans,t);
        if(Y1>1) t=dfs(X1,Y1-1,X2,Y2,Which,Step),ans=max(ans,t);
        if(Y1<n) t=dfs(X1,Y1+1,X2,Y2,Which,Step),ans=max(ans,t);
    }
    else//如果当前为黑棋操作
    {
        if(X2>1) t=dfs(X1,Y1,X2-1,Y2,Which,Step),ans=min(ans,t);
        if(X2>2) t=dfs(X1,Y1,X2-2,Y2,Which,Step),ans=min(ans,t);
        if(X2<=n-1) t=dfs(X1,Y1,X2+1,Y2,Which,Step),ans=min(ans,t);
        if(X2<=n-2) t=dfs(X1,Y1,X2+2,Y2,Which,Step),ans=min(ans,t);
        if(Y2>1) t=dfs(X1,Y1,X2,Y2-1,Which,Step),ans=min(ans,t);
        if(Y2>2) t=dfs(X1,Y1,X2,Y2-2,Which,Step),ans=min(ans,t);
        if(Y2<=n-1) t=dfs(X1,Y1,X2,Y2+1,Which,Step),ans=min(ans,t);
        if(Y2<=n-2) t=dfs(X1,Y1,X2,Y2+2,Which,Step),ans=min(ans,t);
    }
    return res[X1][Y1][X2][Y2][Which^1][Step-1]=ans+1;
}
int main()
{
    read(n),read(X1),read(Y1),read(X2),read(Y2);
    if(abs(X1-X2)+abs(Y1-Y2)<=1) return puts("WHITE 1"),0;//特判白棋获胜的情况
    return pc('B'),pc('L'),pc('A'),pc('C'),pc('K'),pc(' '),write(dfs(X1,Y1,X2,Y2,0,1)),0;
}

转载于:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3106.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值