CQOI2013 棋盘游戏

题目大意

在一个 NN(N2) 的棋盘上有一颗白棋子和一颗黑棋子。白棋子每一回合能向上下左右走一步,黑棋能向上下左右走一步或者2步(每一回合只能向一个方向走)。若一个棋子覆盖了另外一个的话,他就赢了。问你在最优方案下,谁会赢。当然,每个棋子都会用最优策略,也就是说,若一个人知道他一定会输,他会尽量延迟结束。否则他会尽快使自己获得胜利。白棋先走

两个棋子的初始位置会给出。询问你谁会胜利,并且需要多少回合。
N20

解题思路

首先我们发现若一开始不是白棋就在黑子隔壁,它可以一步吃掉黑棋的话,黑棋绝对会获得胜利。

设最终的答案为 Ans .我们可以得到 Ans4N

证明

设白棋坐标为 (r,c) ,黑棋坐标为 (x,y)
不妨假设 rx,cy 其他情况是可以通过旋转这个棋盘而得到的。
我们设白棋走一回合,黑棋走一回合总称为一次。

我们考虑一次总共可能出现的情况。
这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

可以发现的是,每走一次,黑棋和白棋之间的曼哈顿距离都会减少1。

又因为两个点初始的曼哈顿距离最大为 2N .所以我们最多会走 2N 次。总共就是 4N 个回合。

得证。

知道我们的 Ans4N 后,这题就变得简单了。

我们设 (dep,r,c,x,y,ctr) 为一个状态,表示从初始状态开始,走了 dep 个回合,当前白点在 (r,c) ,黑点在 (x,y) ,当前是由 ctr 这个点在做。若为0表示白点先手,1表示黑点先手。

Fstatus
ctr=0 ,则表示在 status 这个状态时,黑棋至多要多少回合才能赢得游戏。
ctr=1 ,则表示黑棋至少多少回合赢得游戏。

那么 Fstatus 的转移是很简单的。

ctr=0 , Fstatus=max(FNextStatus)+1
ctr=1,Fstatus=min(FNextStatus)+1

我们这里可以用记忆化搜索来做。

而且若当前 status dep>4N 时,这个状态其实是没有用的,因为我们已经知道了我们最优解肯定是 4N 的。

复杂度是 O(8N5)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 25;
const int xy[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
const int x2[4][2] = {{2,0},{0,2},{-2,0},{0,-2}};

int F[105][MAXN][MAXN][MAXN][MAXN][2],N,step;

int Dfs(int r1,int c1,int r2,int c2,int ctr,int dep)
{
    if (r1 == r2 && c1 == c2)
    {
        if (ctr == 0) return F[dep][r1][c1][r2][c2][ctr] = 0;
        return F[dep][r1][c1][r2][c2][ctr] = -2;
    }
    if (dep > step) return -2;
    if (F[dep][r1][c1][r2][c2][ctr] != -1) return F[dep][r1][c1][r2][c2][ctr];
    if (!ctr)
    {
        int pt = 0;
        for(int i = 0;i < 4;i ++)
        {
            int nx = r1 + xy[i][0],ny = c1 + xy[i][1];
            if (nx && ny && nx <= N && ny <= N)
            {
                int tmp = Dfs(nx,ny,r2,c2,!ctr,dep + 1);
                if (tmp == -2) {pt = -2;break;}
                pt = max(pt,tmp + 1);   
            }
        }
        return F[dep][r1][c1][r2][c2][ctr] = pt;
    } else
    {
        int pt = (1 << 30);
        for(int i = 0;i < 4;i ++)
        {
            int nx = r2 + xy[i][0],ny = c2 + xy[i][1];
            if (nx && ny && nx <= N && ny <= N)
            {
                int tmp = Dfs(r1,c1,nx,ny,!ctr,dep + 1);
                if (tmp == -2) continue;
                pt = min(pt,tmp + 1);   
            }
        }
        for(int i = 0;i < 4;i ++)
        {
            int nx = r2 + x2[i][0],ny = c2 + x2[i][1];
            if (nx && ny && nx <= N && ny <= N)
            {
                int tmp = Dfs(r1,c1,nx,ny,!ctr,dep + 1);
                if (tmp == -2) continue;
                pt = min(pt,tmp + 1);   
            }
        }
        if (pt == (1 << 30)) pt = -2;
        return F[dep][r1][c1][r2][c2][ctr] = pt;
    }
}

int main()
{
    int r1,c1,r2,c2;
    scanf("%d%d%d%d%d", &N, &r1, &c1, &r2, &c2);
    if (abs(r1 - r2) + abs(c1 - c2) <= 1) printf("WHITE 1\n"); else
    {
        step = 4 * N;
        {
            for(int j = 0;j <= step;j ++) memset(F[j],255,sizeof F[j]);
            Dfs(r1,c1,r2,c2,0,0);
            if (F[0][r1][c1][r2][c2][0] >= 0) {printf("BLACK %d\n", F[0][r1][c1][r2][c2][0]);return 0;}
        }
        printf("DRAW\n");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值