[九省联考2018]一双木棋chess

题目描述

菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。

落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。

棋盘的每个格子上,都写有两个非负整数,从上到下第i 行中从左到右第j 列的格 子上的两个整数记作Ai,jA_{i,j}Ai,jBi,jB_{i,j}Bi,j 。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的Ai,jA_{i,j}Ai,j 之和,牛牛的得分是所有有白棋的格子上的Bi,jB_{i,j}Bi,j的和。

菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。

输入输出格式

输入格式:

从文件chess.in 中读入数据。

输入第一行包含两个正整数n;m,保证n;m <= 10。

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第一个非负整数:其中第i 行中第j 个数表示Ai,jA_{i,j}Ai,j

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第二个非负整数:其中第i 行中第j 个数表示Bi,jB_{i,j}Bi,j

输出格式:

输出到文件chess.out 中。

输出一个整数,表示菲菲的得分减去牛牛的得分的结果。

输入输出样例

输入样例#1: 复制
2 3
2 7 3
9 1 2
3 7 2
2 3 1
输出样例#1: 复制
2

说明

样例1说明:

棋盘如图所示,双方都采用最优策略时,棋局如下:

• 菲菲下在第1 行第1 列(这是第一步时唯一可以落子的格子);

• 牛牛下在第1 行第2 列;

• 菲菲下在第2 行第1 列;

• 牛牛下在第1 行第3 列;

• 菲菲下在第2 行第2 列;

• 牛牛下在第2 行第3 列(这是这一步时唯一可以落子的格子);

• 填满棋盘,游戏结束,盘面如下。

菲菲的得分为:2 + 9 + 1 = 12 ;牛牛的得分为:7 + 2 + 1 = 10 。

对于所有的测试数据,n;m <= 10 ,Ai,jA_{i,j}Ai,j; Bi,jB_{i,j}Bi,j <= 100000。

对于编号为奇数的测试点,保证所有的Bi,jB_{i,j}Bi,j = 0 。


省选时被吊打的画面历历在目...

注意到它一定是一个阶梯状的状态,所以我们hash记录一下每一行填到的位置。

设f[i]表示hash值为i的状态之后的先手减后手的最大值/最小值,因为一个状态唯一确定一个接下来操作的人,所以实现上不用管它最大还是最小。

显然先手要获得$large max(f[s'] + a[i][j])$, 后手要获得$large min(f[s'] - b[i][j])$.

于是min-max对抗搜索。

但是状态量极大数组开不下,考虑到有用的状态不多,所以用map存储。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
#define reg register
#define int long long
inline int read() {
    int res=0;char ch=getchar();bool fu=0;
    while(!isdigit(ch)) {if(ch=='-')fu=1;ch=getchar();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return fu?-res:res;
}
#define base 12
int n, m; 
int a[15][15], b[15][15];
int jie[15];
int END;
inline int hash() {
    int res = 0;
    for (reg int i = 0 ; i <= n ; i ++) 
        res = res * base + jie[i];
    return res;
}

map <int, int> f;
int ans;

int dfs(int opt, int sit) 
{
    if (f.find(sit) != f.end()) return f[sit];
    int tmp = sit;
    for (reg int i = n ; i >= 1 ; i --) jie[i] = tmp % base, tmp /= base;
    if (!opt) { //the first person 
        int res = -1e9;
        for (reg int i = 1 ; i <= n ; i ++)
        {
            if (jie[i] == m) continue;
            if (jie[i] < jie[i-1]) {
                jie[i]++;
                int sum = a[i][jie[i]] + dfs(opt ^ 1, hash());
                res = max(res, sum);
                jie[i]--;
            }
        }
        return f[sit] = res;
    } else {
        int res = 1e9;
        for (reg int i = 1 ; i <= n ; i ++)
        {
            if (jie[i] == m) continue;
            if (jie[i] < jie[i-1]) {
                jie[i]++;
                int sum = dfs(opt ^ 1, hash()) - b[i][jie[i]];
                res = min(res, sum);
                jie[i]--;
            }
        }
        return f[sit] = res;        
    }
}

signed main()
{
    n = read(), m = read();
    for (reg int i = 1 ; i <= n ; i ++)
        for (reg int j = 1 ; j <= m ; j ++)
            a[i][j] = read();
    for (reg int i = 1 ; i <= n ; i ++)
        for (reg int j = 1 ; j <= m ; j ++)
            b[i][j] = read();
    jie[0] = m + 1;
    for (reg int i = 1 ; i <= n ; i ++) jie[i] = m;
    int END = hash();
    f[END] = 0;
    cout << dfs(0, 0) << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/BriMon/p/9575685.html