[CQOI2013]棋盘游戏

题意

一个n*n(n>=2)棋盘上有黑白棋子各一枚。游戏者A和B轮流移动棋子,A先走。
A的移动规则:只能移动白棋子。可以往上下左右四个方向之一移动一格。
B的移动规则:只能移动黑棋子。可以往上下左右四个方向之一移动一格或者两格。
和通常的“吃子”规则一样,当某游戏者把自己的棋子移动到对方棋子所在的格子时,他就赢了。两个游戏者都很聪明,当可以获胜时会尽快获胜,只能输掉的时候会尽量拖延时间。你的任务是判断谁会赢,需要多少回合。

n20

分析

若一开始白棋在黑棋周围一格,则白棋可以获胜,否则白棋显然不可能获胜。
然后我们有一个非常重要的结论,黑棋获胜的步数 Ans4n
证明详见http://blog.csdn.net/PhilipsWeng/article/details/47841649
这样我们可以限深记忆化搜索,步数 dep>4n 的状态我们可以不做。
f[dep][x1][y1][x2][y2] 为过了 dep 个回合,白棋在 (x1,y1) ,黑棋在 (x2,y2) 的状态。对于当前是白棋走(dep是偶数),它肯定不能获胜,则它肯定会选择黑棋最慢获胜的后继状态,状态记录的是最久还需几个回合黑棋才会获胜;反之,当前是黑棋走(dep是奇数),它会选择它最快获胜的后继状态,该状态为最快还需几个回合黑棋就会获胜。
那么转移方程就很容易推出了。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 22;
const int flag[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int f[4 * N][N][N][N][N],n,lim,x1,y1,x2,y2;

int dfs(int dep,int x1,int y1,int x2,int y2) {
    if (f[dep][x1][y1][x2][y2] != -1) return f[dep][x1][y1][x2][y2];
    if (dep > lim) return -2;
    if (x1 == x2 && y1 == y2) {
        if (dep & 1) return -2;
        return 0;
    }
    if (dep & 1) {
        int re = (1 << 30);
        for (int i = 0;i < 4;i ++) {
            int x = x2 + flag[i][0],y = y2 + flag[i][1];
            if (x < 1 || y < 1 || x > n || y > n) continue;
            int cur = dfs(dep + 1,x1,y1,x,y);
            if (cur == -2) continue;
            re = min(re,cur + 1);
        }
        for (int i = 0;i < 4;i ++) {
            int x = x2 + 2 * flag[i][0],y = y2 + flag[i][1] * 2;
            if (x < 1 || y < 1 || x > n || y > n) continue;
            int cur = dfs(dep + 1,x1,y1,x,y);
            if (cur == -2) continue;
            re = min(re,cur + 1);
        }
        if (re == (1 << 30)) re = -2;
        return f[dep][x1][y1][x2][y2] = re;
    }
    else {
        int re = 0;
        for (int i = 0;i < 4;i ++) {
            int x = x1 + flag[i][0],y = y1 + flag[i][1];
            if (x < 1 || y < 1 || x > n || y > n) continue;
            int cur = dfs(dep + 1,x,y,x2,y2);
            if (cur == -2) return f[dep][x1][y1][x2][y2] = -2;
            re = max(re,cur + 1);
        }
        return f[dep][x1][y1][x2][y2] = re;
    }
}

int main() {
    scanf("%d%d%d%d%d",&n,&x1,&y1,&x2,&y2);
    if (abs(x1 - x2) + abs(y1 - y2) <= 1) printf("WHITE 1\n"); 
    else{
        lim = 4 * n;
        memset(f,255,sizeof(f));
        int ans = dfs(0,x1,y1,x2,y2);
        if (ans >= 0) printf("BLACK %d\n",ans);
        else printf("DRAW\n");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值