CQOI2013 棋盘游戏

题目大意

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

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

解题思路

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

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

证明

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

我们考虑一次总共可能出现的情况。
CQOI2013 棋盘游戏_第1张图片

CQOI2013 棋盘游戏_第2张图片

CQOI2013 棋盘游戏_第3张图片

CQOI2013 棋盘游戏_第4张图片

可以发现的是,每走一次,黑棋和白棋之间的曼哈顿距离都会减少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");
    }
}

你可能感兴趣的:(CQOI2013 棋盘游戏)