【暴搜博弈+记忆化搜索神题】 UVA 10838——The Pawn Chess

The Pawn Chess

Input: Standard Input

Output: Standard Output

Consider the following mini-version of chess: We have a 4x4 chessboard, with four white pawns on the first rank (bottom line in the input) and four black pawns on the last rank. The goal is for the player to get one of his pawns to the other end of the board (last rank for the white player, first rank for the black player), or to stalemate his opponent. A player is stalemated if he has no legal moves when it's his turn to move (this includes having no pawns left).

The pawns move as in ordinary chess, except that they can never move two steps. That is, a pawn may either move one step forward (toward the opposite rank), assuming that this square is empty. A pawn can also capture a pawn of the opposite color if it's one step ahead and to the left or right. Captured pieces are removed from the game.

Given the position of the pawns on a chessboard, determine who will win the game assuming both players play optimally. You should also determine the number of moves the game will last before it's decided (assuming the player that will win tries to win as fast as possible and the player to lose will lose as slow as possible). It will always be white's turn to move from the given position.

Input

The first line in the input contains the number of test cases (at most 50). Each case contains four lines describing the chessboard, preceeded by a blank line. The first of the four lines will be the last rank of the chessboard (the starting point for the black pawns). Black pawns will be denoted with a 'p', white pawns with a 'P', and empty squares with a '.'. There will be between one and four pawns of each color. The inital position will not be a final game position, and white will always have at least one legal move. Note that the input position may not necessarily be one that could have arisen from legal play from the games starting position.

 

Output

For each test case, output a line containing the text white (xx) if white will win, or black (xx) if black will win. Replace xx with the number of moves (which will always be an odd number if white wins and an even number if black wins).

 

Sample Input                                 Output for Sample Input

2
 
.ppp
....
.PPP
....
 
...p
...p
pP.P
...P
 
white (7)
black (2)

发这篇题解的原因实在是因为网上根本没有这个题的题解,看了一上午我也是没明白,这个算法在国内的ACM相关模板中也是没有出现过的。

我能做的只是翻译一下WORLD FINAL大神的思路。

这是一个标准的最大最小博弈问题,可以套用模板。(但是我翻遍了所有模板,根本没有。。

通过枚举,我们可以得出结论,这个问题本身的复杂度小于3的16次方,因为只有三种情况,p,P,和点。开一个数组给每一种情况。所以, 可以把一个数组当做白方胜利之前所需要走的步数,黑方同理。每一步的状态都是一个基于3为底数编码的数,其中每个数字与棋盘上点的格排布相对应(我的理解是压缩后以三进制方式存下每一步,即将每一步的图存成一个数字节省空间,然后枚举图,可能是记忆化搜索的一个特征

table[s] > 0 意味着白方将在s状态下走w[s]步后胜利。
table[s] < 0 意味着白方将在s状态下走-w[s]步后失败。
table[s] = 0 意味着白方没有任何可以用的走法了。

说白了就是一句话:

Write a recursive function that takes a state, finds all possible moves, and chooses the best one. Memoize your results in w[] and b[]. The two base cases for your recursion are:
写一个函数储存状态,寻找所有可行移动情况,选一个最好的,在数据中记录下结果,能动返回1,不能动返回0。(看起来很好懂,但是实现起来,好吧,上当了……

大神的可解代码,目前网上没有放出任何该题的题解。

UVA上通过的提交据说全是用的记忆化搜索+进制压缩+ab剪枝,版权归wesley所有,希望有大神能够再开博客具体讲解其具体实现思路,不吝感谢):

另附两个不是很详细的讲解:点击打开链接(极大极小搜索过程)点击打开链接(一些类似相关题目)

花了一上午,虽不明,也算值了。。。。

 

#include <stdio.h>
#include <string.h>

#define GET(a,i) (((a) >> (i)) & 1)
#define BIT(i) (1 << (i))

static unsigned enc16[65536], enc8[256], enc8_m2[256];
static signed char table[39203 * 70 * 2 + 1024];

static void mktab()
{
    static int a[16];
    int i, j, k, m;
    enc16[0] = 0;
    m = 1;
    for (a[0] = -1, k = 0; k >= 0;)
    {
        if (++a[k] >= 16)
        {
            k--;
            continue;
        }
        for (i = 0, j = 0; i <= k; i++)
            j |= BIT(a[i]);
        enc16[j] = m++;
        if (k < 7)
        {
            a[k + 1] = a[k];
            k++;
        }
    }
    m = 0;
    for (a[0] = -1, k = 0; k >= 0;)
    {
        if (++a[k] >= 8)
        {
            k--;
            continue;
        }
        for (i = 0, j = 0; i <= k; i++)
            j |= BIT(a[i]);
        enc8[j] = m;
        if (k < 3)
        {
            a[k + 1] = a[k];
            k++;
        }
        else
        {
            m++;
        }
    }
    for (k = 0; k < 256; k++)
    {
        for (i = 0, j = 0; i < 8; i++)
            j += (k >> i) & 1;

        if (j > 4)
            continue;
        for (i = k; j < 4; j++)
            i |= (1 << (j + 4));
        enc8[k] = enc8[i];
    }
    for (k = 0; k < 256; k++)
        enc8_m2[k] = enc8[k] << 1;
}

static unsigned encode(unsigned w, unsigned b)
{
    register int i, r, s;
    s = (b | w);
    for (i = 0, r = 0; s; s >>= 1, i++)
        if (s & 1) r = (r << 1) | ((w >> i) & 1);
    return enc16[w | b] * 140 + enc8_m2[r];
}

static int playb(unsigned w, unsigned b);
static int playw(unsigned w, unsigned b)
{
    unsigned cfg;
    int win, lose, k, s, t;

    cfg = encode(w, b);

    if (table[cfg] != 0)
        return table[cfg];

    if ((b & 0x000F) || (w == 0))
        return (table[cfg] = -1);

    s = w | b;

    for (win = lose = 0, k = 0; k < 12; k++)
    {
        if (GET(w, k) == 0)
            continue;

        if (GET(s, k + 4) == 0)
        {
            t = playb(w ^ BIT(k) ^ BIT(k+4), b);
            if (t > 0 && (win == 0 || t < win))
            {
                win = t;
                if (win == 1) break;
            }
            else if (t < 0 && t < lose)
            {
                lose = t;
            }
        }

        if ((k & 3) != 3 && GET(b, k + 5))
        {
            t = playb(w ^ BIT(k) ^ BIT(k+5), b ^ BIT(k+5));
            if (t > 0 && (win == 0 || t < win))
            {
                win = t;
                if (win == 1) break;
            }
            else if (t < 0 && t < lose)
            {
                lose = t;
            }
        }

        if ((k & 3) != 0 && GET(b, k + 3))
        {
            t = playb(w ^ BIT(k) ^ BIT(k+3), b ^ BIT(k+3));
            if (t > 0 && (win == 0 || t < win))
            {
                win = t;
                if (win == 1) break;
            }
            else if (t < lose)
            {
                lose = t;
            }
        }
    }

    if (win != 0)
        return table[cfg] = win + 1;
    else
        return table[cfg] = lose - 1;
}

static int playb(unsigned w, unsigned b)
{
    unsigned cfg;
    int win, lose, k, s, t;

    cfg = encode(w, b) | 1;

    if (table[cfg] != 0)
        return table[cfg];

    if ((w & 0xF000) || b == 0)
        return (table[cfg] = 1);

    s = w | b;

    for (win = lose = 0, k = 4; k < 16; k++)
    {
        if (GET(b, k) == 0)
            continue;

        if (GET(s, k - 4) == 0)
        {
            t = playw(w, b ^ BIT(k) ^ BIT(k-4));
            if (t < 0 && (win == 0 || win < t))
            {
                win = t;
                if (win == -1) break;
            }
            else if (lose < t)
            {
                lose = t;
            }
        }

        if ((k & 3) != 3 && GET(w, k - 3))
        {
            t = playw(w ^ BIT(k-3), b ^ BIT(k) ^ BIT(k-3));
            if (t < 0 && (win == 0 || win < t))
            {
                win = t;
                if (win == -1) break;
            }
            else if (lose < t)
            {
                lose = t;
            }
        }

        if ((k & 3) != 0 && GET(w, k - 5))
        {
            t = playw(w ^ BIT(k-5), b ^ BIT(k) ^ BIT(k-5));
            if (t < 0 && (win == 0 || win < t))
            {
                win = t;
                if (win == -1) break;
            }
            else if (lose < t)
            {
                lose = t;
            }
        }
    }

    if (win != 0)
        return table[cfg] = win - 1;
    else
        return table[cfg] = lose + 1;
}

int main()
{
    static int w, b, i, c, t;

    mktab();

    for (scanf("%d", &t) == 1; t-- > 0;)
    {
        for (w = b = 0, i = 15; i >= 0 && (c = getchar()) != EOF;)
        {
            if (c == '.')
                i--;
            else if (c == 'P')
                w |= (1 << i--);
            else if (c == 'p')
                b |= (1 << i--);
        }

        i = playw(w, b);

        if (i < 0)
            printf("black (%d)\n", -i - 1);
        else
            printf("white (%d)\n", i - 1);
    }

    return 0;
}


 

 

posted @ 2013-04-05 12:33  坚固66  阅读(159)  评论(0编辑  收藏  举报