[BZOJ5248][2018多省省队联测]一双木棋(状压dp)

本文介绍了一种使用动态规划解决特殊棋盘游戏的方法。通过定义状态转移方程,利用map存储中间状态,实现了对任意合法状态的有效计算。具体地,文章详细解释了如何根据棋盘上棋子的分布来最大化先手玩家相对于后手玩家得分的优势。

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=5248

Solution

容易想到 dp :
f[S] f [ S ] 表示当前棋子的状态为 S S ,从状态 S 到棋盘被摆满的过程中, 先 手 得 分 − 后 手 得 分 的最大值。
考虑如何存下这个状态。
看到落子的规则,可以发现任何时候棋盘都满足:
(1)有棋子的行一定是前面的几行。
(2)在每行内,有棋子的格子一定是前面的几格。
(3)一行内的棋子个数随行号单调不增。
所以用 map 存转移,合法的 S S 是可以承受的。
|S| 为偶数时,转移:

f[S]=max{Ai,jf[add(S,i)]} f [ S ] = max { A i , j − f [ add ( S , i ) ] }

否则 |S| | S | 为奇数:
f[S]=max{Bi,jf[add(S,i)]} f [ S ] = max { B i , j − f [ add ( S , i ) ] }

i i 为枚举的行号。
j 表示第 i i 行的最后一个棋子所在列号。
add(S,i) 表示在状态 S S i 行第 j+1 j + 1 列放棋子达到的状态。

Code

#include <map>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 11, INF = 0x3f3f3f3f;
int n, m, a[N][N], b[N][N], suma[N][N], sumb[N][N];
struct cyx {
    int x[N];
    friend inline bool operator == (cyx a, cyx b) {
        int i; For (i, 1, n) if (a.x[i] != b.x[i]) return 0;
        return 1;
    }
} emp, eds;
int orz(cyx state) {
    int i, res = 0; For (i, 1, n) res += state.x[i];
    return res & 1;
}
ll encode(cyx state) {
    int i; ll res = 0;
    For (i, 1, n) (res *= m) += state.x[i] - 1;
    return res;
}
map<ll, int> f, w;
int dp(cyx state) {
    ll sta = encode(state);
    if (state == eds) return w[sta] = 1, f[sta] = 0;
    if (w[sta]) return f[sta];
    int i, res = -INF, p = orz(state);
    For (i, 1, n)
        if ((i == 1 || state.x[i] < state.x[i - 1])
        && state.x[i] < m) {
            cyx ns = state; ns.x[i]++;
            res = max(res, (p ? b[i][ns.x[i]]
                : a[i][ns.x[i]]) - dp(ns));
        }
    return w[sta] = 1, f[sta] = res;
}
int main() {
    int i, j; 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) Rof (j, m, 1) suma[i][j] = suma[i][j + 1] + a[i][j];
    For (i, 1, n) Rof (j, m, 1) sumb[i][j] = sumb[i][j + 1] + b[i][j];
    For (i, 1, n) eds.x[i] = m;
    cout << dp(emp) << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值