【BZOJ5248】【2018九省联考】一双木棋 chess (DP,哈希)

本文介绍了一款基于棋盘的游戏,玩家需遵循特定规则放置棋子并计算得分。通过使用动态规划方法解决该问题,文章详细解释了状态表示、哈希函数设计以及递归搜索策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。棋盘的每个格子上,都写有两个非负整数,从上到下第i行中从左到右第j列的格子上的两个整数记作Aij、Bij。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的Aij之和,牛牛的得分是所有有白棋的格子上的Bij的和。
菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。


Solution

由于每一行的棋子不能超过上一行,所以状态数非常少,大概是十万级别的,考虑dp。
对于每一个状态我们哈希一下,存在maphash_table里面,直接转移就行了。


Code

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>

using namespace std;
using namespace __gnu_pbds;

#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define SZ(a) ((int)(a.size()))
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define INF (0x3f3f3f3f)

typedef long long LL;
typedef pair<int, int> PII;

template<typename T> bool chkmin(T& a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> bool chkmax(T& a, T b) { return a < b ? a = b, 1 : 0; }

inline int read()
{
    register int _ = 0, __ = 1; register char c_;
    for (c_ = getchar(); !isdigit(c_); c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; isdigit(c_); c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ - 48);
    return _ * __;
}

const int maxn = 13;

struct Data
{
    int a[maxn];
}a;

cc_hash_table<LL, int> dp;
int A[maxn][maxn], B[maxn][maxn], n, m;
queue<Data> q;

inline LL Hash(Data a)
{
    LL hash = 0;
    For(i, 1, n) (hash *= 11ll) += a.a[i];
    return hash;
}

int dfs(Data a)
{
    int t = Hash(a);
    if (dp[t]) return dp[t];
    int tmp, cnt = 0, mark = 1;
    For(i, 1, n) {
        cnt += (a.a[i] & 1);
        if (a.a[i] != m) mark = 0;
    }
    if (mark) return 0;
    cnt &= 1;
    cnt ^= 1;
    if (cnt) tmp = -INF; else tmp = INF;
    For(i, 1, n)
    {
        if (a.a[i] + 1 <= a.a[i - 1])
        {
            ++ a.a[i];
            if (!cnt) chkmin(tmp, dfs(a) - B[i][a.a[i]]);
            else chkmax(tmp, dfs(a) + A[i][a.a[i]]);
            -- a.a[i];
        }
    }
    //For(i, 1, n) cout << a.a[i] << ' ';
    //cout << tmp << endl;
    dp[t] = tmp;
    return tmp;
}

int main()
{

    n = read(), m = read();
    For(i, 1, n) For(j, 1, m) A[i][j] = read();
    For(i, 1, n) For(j, 1, m) B[i][j] = read();

    For(i, 1, n) a.a[i] = 0;
    a.a[0] = m;
    printf("%d\n", dfs(a));

    return 0;
}
//风急天高猿啸哀,渚清沙白鸟飞回。
//    -- 杜甫《登高》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值