CQOI2013 棋盘游戏

来源:互联网 发布:淘宝ipad横版 编辑:程序博客网 时间:2024/06/06 19:23

题目大意

在一个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

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

而且若当前statusdep>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 0
原创粉丝点击