[BZOJ5248] 2018九省联考 D1T1 一双木棋 | 博弈论 状压DP
题面
菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。
落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。
棋盘的每个格子上,都写有两个非负整数,从上到下第i 行中从左到右第j 列的格 子上的两个整数记作\(A_{i, j}\)、\(B_{i, j}\)。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的\(A_{i, j}\)之和,牛牛的得分是所有有白棋的格子上的\(B_{i, j}\)的和。
菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。
题解
简单题。考场上的我是傻逼。
状压DP是容易想到的思路。若\(s\)是一个状态(先不管它是怎么压出来的),若\(s\)状态中有偶数个格子有棋子(现在轮到A下了),\(f[s]\)表示该状态之后的A得分与B得分之差的最大值(因为A想让这个得分差尽可能大);反之,若\(s\)中有奇数个格子有棋子,则轮到B下了,\(f[s]\)表示该状态之后的A得分与B得分之差的最小值。
注意\(f[s]\)表示的是达到状态\(s\)之后的落子造成的得分差,而不是之前的!因为两个人的决策显然是根据【未来】做出的,而不是过去……(所以应该倒着枚举\(s\))
首先,一个格子能落棋子当且仅当:【棋盘左上角到该格子】这个矩形中,只有该格子是空的。
那么每时每刻,有棋子的格子和空格子的分布一定是形如这样的('#'代表有棋子,'-'代表无棋子)
######
###---
##----
##----
#-----
这启发了我们如何进行状压。
想象'#'与'-'的分界线,像这样:
___|
_|
|
_|
_|
显然,分界线永远从左下走向右上,期间只【向左走】或【向上走】。
用0和1表示分界线的形状:1表示向上走一格,0表示向右走一格。
例如这个局面
####
###
###
#
它的分界线形状可以表示为01001101
,是一个长为\(n + m\)的01串。
那么把01串作为二进制数来表示状态。由于串中一定有且只有\(n\)个1,所以总共有效的状态只有\(C_{n + m}^{n}\)个,但是直接\(2^{n + m}\)枚举就可以通过了。
总结一下:由大到小枚举状态\(s\),每次把其中的一个10
改为01
(这样分界线向外弯了一格,设这格为\((x, y)\)),设新状态为\(t\),则若状态\(s\)中'#'有偶数个,\(f[s] = max\{f[t] + A_{x, y}\}\),否则\(f[s] = min\{f[t] - B_{x, y}\}\)。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define enter putchar('\n')
#define space putchar(' ')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op == 1) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 15, INF = 0x3f3f3f3f;
int n, m, a[N][N], b[N][N], f[1 << 20];
void debug(int x){
for(int i = 0; i < (n + m); i++)
putchar('0' + (x >> i & 1));
}
int main(){
read(n), read(m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
read(a[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
read(b[i][j]);
for(int s = (1 << (n + m)) - 1, fi = 1; s >= 0; s--){
int cnt1 = 0, area = 0;
for(int i = 0; i < n + m; i++)
if(s >> i & 1) cnt1++;
if(cnt1 != n) continue;
if(fi){
fi = 0;
continue;
}
for(int i = 0, x = n + 1, y = 1; i < n + m; i++){
if(s >> i & 1) x--;
else if(y++ <= m) area += x - 1;
}
f[s] = (area & 1) ? INF : -INF;
for(int i = 0, x = n + 1, y = 1; i < n + m; i++){
if(s >> i & 1) x--;
else{
if(i && (s >> (i - 1) & 1)){
if(area & 1) f[s] = min(f[s], f[s ^ (3 << (i - 1))] - b[x][y]);
else f[s] = max(f[s], f[s ^ (3 << (i - 1))] + a[x][y]);
}
y++;
}
}
}
write(f[(1 << n) - 1]), enter;
return 0;
}
[BZOJ5248] 2018九省联考 D1T1 一双木棋 | 博弈论 状压DP的更多相关文章
- [BZOJ5248][2018九省联考]一双木棋
题目描述 https://www.lydsy.com/JudgeOnline/problem.php?id=5248 Solution 我们首先考虑放棋子的操作 发现它一定放棋子的部分是一个联通块 ...
- bzoj5248 [2018多省省队联测]一双木棋
直接hash+爆搜即可. #include <cstdio> #include <cstring> #include <iostream> #include < ...
- bzoj千题计划307:bzoj5248: [2018多省省队联测]一双木棋
https://www.lydsy.com/JudgeOnline/problem.php?id=5248 先手希望先手得分减后手得分最大,后手希望先手得分减后手得分最小 棋盘的局面一定是阶梯状,且从 ...
- bzoj5248(洛谷4363)(2018九省联考)一双木棋
题目:https://www.luogu.org/problemnew/show/P4363 一种考虑状态数的方法:有几个用了k个格子的列,就在第k个0的左边插入几个1: 这也是求不降序列的个数的方法 ...
- BZOJ5248:[九省联考2018]一双木棋——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=5248 https://www.luogu.org/problemnew/show/P4363#su ...
- 九省联考2018 D1T1 一双木棋
Alice和Bob轮流在n*m的棋盘上放棋子 a[i][j]表示Alice放在这的收益,b[i][j]表示Bob放在这的收益 一个地方没有棋子且它的左边上边都有棋子才能放棋子,边界外视为有一圈棋子 n ...
- 2018九省联考(SHOI2018)
听说在退役前还能有去外省的机会QAQ D1 9点T1,T2过拍,感觉自己稳得一批,然后边看T3边幻想AK 事实证明我是多么菜多么无知多么傻逼 想T3时太浮躁,最后也没想出来 T2根本没有想过去怀疑自己 ...
- bzoj 5248: [2018多省省队联测]一双木棋
Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...
- 2018.11.06 bzoj1097: [POI2007]旅游景点atr(最短路+状压dp)
传送门 预处理出不能在每个点停留之后才停留的点的状态. 对kkk个点都跑一次最短路存下来之后只需要简单状压一下就能过了吐槽原题空间64MB蒟蒻无能为力 然后用fillfillfill赋极大值的时候当m ...
随机推荐
- LInq之Take Skip TakeWhile SkipWhile Reverse Union Concat 用法
废话不多说,直接上代码,代码有注释!自行运行测试! class Program { static void Main(string[] args) { string[] names = { " ...
- 在平衡树的海洋中畅游(一)——Treap
记得有一天翔哥毒奶我们: 当你们已经在平衡树的海洋中畅游时,我还在线段树的泥沼中挣扎. 我觉得其实像我这种对平衡树一无所知的蒟蒻也要开一开数据结构了. 然后花了一天啃了下最简单的平衡树Treap,感觉 ...
- getUserMedia API及HTML5 调用摄像头和麦克风
getUserMedia API简介 HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头.视频.音频.地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件 ...
- M2 终审
1.团队成员简介 左边:马腾跃 右边:陈谋 左上:李剑锋 左下:仉伯龙 右:卢惠明 团队成员及博客: 李剑锋: Blog: http://www.cnblogs.com/Po ...
- [BUAA软工]第二次博客作业---结对编程
[BUAA软工]结对作业 项目 内容 这个作业属于哪个课程 北航软工 这个作业的要求在哪里 2019年软件工程基础-结对项目作业 我在这个课程的目标是 学习如何以团队的形式开发软件,提升个人软件开发能 ...
- S2X环境搭建与示例运行
S2X环境搭建与示例运行 http://dbis.informatik.uni-freiburg.de/forschung/projekte/DiPoS/S2X.html 环境 Maven proje ...
- 《Linux内核分析》第六周笔记 进程的描述和进程的创建
进程的描述和进程的创建 一.进程的描述 1.进程描述符task_struct数据结构(一) 操作系统的三大功能:进程管理(核心).内存管理.文件系统. 进程控制块PCB——task_struct(进程 ...
- 20135337——Linux实践三:ELF文件格式(64位系统,简单分析)
ELF文件格式简单分析 (具体分析见上一篇ELF文件格式32位系统) ELF-header 第一行: 457f 464c :魔数: 0201 :64位系统,小端法 01 :文件头版本 剩余默认0: 第 ...
- Daemon Thread [shirocache] (Running)
shiro cache的原理 https://cwiki.apache.org/confluence/display/SHIRO/Caching shirocache这个线程相关的内容
- Git—学习笔记1
Git是一种分布式版本控制工具,现阶段比较流行的版本控制工具主要分为:集中式版本控制工具盒分布式版本控制工具. 集中式版本控制工具:SVN和CVS为代表 集中式版本控制系统(每次都得从SVN服务器数据 ...