[BZOJ5248][九省联考2018]一双木棋(连通性DP,对抗搜索)
5248: [2018多省省队联测]一双木棋
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 43 Solved: 34
[Submit][Status][Discuss]Description
菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。棋盘的每个格子上,都写有两个非负整数,从上到下第i行中从左到右第j列的格子上的两个整数记作Aij、Bij。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的Aij之和,牛牛的得分是所有有白棋的格子上的Bij的和。菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何Input
第一行包含两个正整数n,m,保证n,m≤10。接下来n行,每行m个非负整数,按从上到下从左到右的顺序描述每个格子上的第一个非负整数:其中第i行中第j个数表示Aij。接下来n行,每行m个非负整数,按从上到下从左到右的顺序描述每个格子上的第二个非负整数:其中第i行中第j个数表示Bijn, m ≤ 10 , Aij, Bij ≤ 100000Output
输出一个整数,表示菲菲的得分减去牛牛的得分的结果。Sample Input
2 3
2 7 3
9 1 2
3 7 2
2 3 1Sample Output
2HINT
Source
这套卷子真的是CCF卷的风格。。专门克制我这种做题慢如蜗牛的人。。
首先这个题一看范围就差不多了,不是搜索就是DP,要拿满分显然得记忆化。因为博弈的最佳决策是根据后继状态选择的,所以返回的是某个状态之后可能拉出的最大/最小分差。由于先手是想让A-B最大,后手反之,交替进行,所以我们搜索时分两种情况讨论一下,最终一定能得到最优决策下的答案。
至于怎么存储当前状态,直接状压每行放了多少个棋子即可,状态直接转移。我起先是按照11进制存储状态的,发现跑了0.99s,后来改成16进制用位运算代替除法取模,就只要0.29s了。
还有一种方法是连通性DP,将从左下到右上的轮廓线压进二进制里转移(0表示向上,1表示向右)。
方法一:
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=,inf=,P=;
ll H[P],G; int H1[P];
int n,m,a[N][N],b[N][N],p[N]; void inc(int &x){ x++; if (x>=P) x-=P; }
void Hash(ll S,int k){ int x=S%P; while (H[x]) inc(x); H[x]=S; H1[x]=k; }
int Find(ll S){ int x=S%P; while (H[x]!=S && H[x]) inc(x); return H1[x]; } int get(ll S,int x){ if (!x) return m; x=n-x; while (x--) S>>=; return S&; }
ll upd(ll S,int x){
if (x==){
int tot=;
rep(i,,n-) p[tot++]=S&,S>>=; S++;
while (tot--) S=(S<<)+p[tot];
return S;
}
int tot=;
rep(i,,n-x) p[tot++]=S&,S>>=; S++;
while (tot--) S=(S<<)+p[tot];
return S;
} int dfs(ll S,int x){
int t=Find(S); if (t) return t;
if (S==G-) return (x ? -b[n][m] : a[n][m]);
if (x==){
int res=-inf;
rep(i,,n) if (get(S,i)<get(S,i-)) res=max(res,a[i][get(S,i)+]+dfs(upd(S,i),x^));
Hash(S,res); return res;
}else{
int res=inf;
rep(i,,n) if (get(S,i)<get(S,i-)) res=min(res,dfs(upd(S,i),x^)-b[i][get(S,i)+]);
Hash(S,res); return res;
}
} int main(){
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) G=G*+m;
rep(i,,n) rep(j,,m) scanf("%d",&a[i][j]);
rep(i,,n) rep(j,,m) scanf("%d",&b[i][j]);
printf("%d\n",dfs(,));
return ;
}
方法二:
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
using namespace std; const int N=,inf=;
int n,m,a[N][N],b[N][N],f[<<],p[]={}; int cnt(int x){ int res=; for (; x; x>>=) if (x&) res++; return res; }
int work(int x){
int res=,w=;
for (; x; x>>=) if (x&) res+=w; else w++;
return res;
}
int valA(int v,int s){ int g=cnt(v&(p[s]-)); return a[n-s+g][+g]; }
int valB(int v,int s){ int g=cnt(v&(p[s]-)); return b[n-s+g][+g]; } void solve(int S,int x){
int res;
if (x){
res=inf;
for (int i=; i<n+m-; i++)
if (!(p[i]&S) && (p[i+]&S))
res=min(res,f[S^p[i]^p[i+]]-valB(S,i));
}else{
res=-inf;
for (int i=; i<n+m-; i++)
if (!(p[i]&S) && (p[i+]&S))
res=max(res,f[S^p[i]^p[i+]]+valA(S,i));
}
f[S]=res;
} int main(){
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) rep(j,,m) scanf("%d",&a[i][j]);
rep(i,,n) rep(j,,m) scanf("%d",&b[i][j]);
p[]=; rep(i,,n+m) p[i]=p[i-]<<;
f[p[m]-]=;
rep(i,p[m],p[n+m]-p[n])
if (cnt(i)==m) solve(i,(n*m-work(i))&);
printf("%d\n",f[p[n+m]-p[n]]);
return ;
}
[BZOJ5248][九省联考2018]一双木棋(连通性DP,对抗搜索)的更多相关文章
- P4363 [九省联考2018]一双木棋chess(对抗搜索+记忆化搜索)
传送门 这对抗搜索是个啥玩意儿…… 首先可以发现每一行的棋子数都不小于下一行,且局面可由每一行的棋子数唯一表示,那么用一个m+1进制数来表示当前局面,用longlong存,开map记忆化搜索 然后时间 ...
- 洛谷 P4363 [九省联考2018]一双木棋chess 解题报告
P4363 [九省联考2018]一双木棋chess 题目描述 菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落 ...
- BZOJ5248:[九省联考2018]一双木棋——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=5248 https://www.luogu.org/problemnew/show/P4363#su ...
- [九省联考2018]一双木棋chess——搜索+哈希
题目:bzoj5248 https://www.lydsy.com/JudgeOnline/problem.php?id=5248 洛谷P4363 https://www.luogu.org/prob ...
- Luogu4363 [九省联考2018]一双木棋chess 【状压DP】【进制转换】
题目分析: 首先跑个暴力,求一下有多少种状态,发现只有18xxxx种,然后每个状态有10的转移,所以复杂度大约是200w,然后利用进制转换的技巧求一下每个状态的十进制码就行了. 代码: #includ ...
- luogu P4363 [九省联考2018]一双木棋chess
传送门 对抗搜索都不会,我真是菜死了qwq 首先根据题目条件,可以发现从上到下每一行的棋子数是单调不增的,然后n m都比较小,如果把状态搜出来,可以发现合法状态并不多,所以可以用一个11进制数表示状态 ...
- [九省联考2018]一双木棋chess
题解: 水题吧 首先很显然的是状压或者搜索 考虑一下能不能状压吧 这个东西一定是长成三角形的样子的 所以是可以状压的 相邻两位之间有几个0代表他们差几 这样最多会有2n 然后就可以转移了 由于之前对博 ...
- 【题解】Luogu P4363 [九省联考2018]一双木棋chess
原题传送门 这道题珂以轮廓线dp解决 经过推导,我们珂以发现下一行的棋子比上一行的棋子少(或等于),而且每一行中的棋子都是从左向右依次排列(从头开始,中间没有空隙) 所以每下完一步棋,棋盘的一部分是有 ...
- P4363 [九省联考2018]一双木棋chess
思路 容易发现只能在轮廓线的拐点处落子,所以棋盘的状态可以用一个n+m长度的二进制数表示 转移就是10变成01 代码 #include <cstdio> #include <algo ...
随机推荐
- 科猫网项目总结(基于SSM框架)
1.配置文件的添加 SSM整合需要web.xml配置文件,springmvc的配置文件,spring和mybatis整合的配置文件. 1.web.xml文件的配置 1.在WEB-INF下新建web.x ...
- 将资源文件夹中的文件通过流的方式写入到应用的File文件夹中
//1.在Files文件夹中创建同名的数据库文件 File files = getFilesDir(); File file = new File(files, DBName); if(file.ex ...
- centos 搭建 ss
download:https://files.cnblogs.com/files/xishaonian/ShadowsocksR-4.7.0-win.7z 使用方法:使用root用户登录,运行以下命令 ...
- C++学习之路(二):引用
(1)引用是变量的别名 引用的基本定义格式:类型 &引用名 = 变量名 例如:int a = 1; int &b = a,这里b是a的别名,b与a都指向了同一块内存单元. 对于引用而言 ...
- skb管理函数之skb_clone、pskb_copy、skb_copy
skb_clone--只复制skb描述符本身,如果只修改skb描述符则使用该函数克隆: pskb_copy--复制skb描述符+线性数据区域(包括skb_shared_info),如果需要修改描述符以 ...
- java===java基础学习(4)---字符串操作
java中的字符串操作和python中的大致相同,需要熟悉的就是具体操作形式. 关于具体api的使用,详见:java===字符串常用API介绍(转) package testbotoo; public ...
- 64_l1
L-function-1.23-18.fc26.i686.rpm 13-Feb-2017 23:19 154562 L-function-1.23-18.fc26.x86_64.rpm 13-Feb- ...
- 浅析MongoDB用户管理
浅析MongoDB用户管理 http://www.jb51.net/article/53830.htm mongodb3.03开启认证 http://21jhf.iteye.com/blog/2216 ...
- 利用keepalive+mysql replication 实现数据库的高可用
利用keepalive+mysql replication 实现数据库的高可用 http://www.xuchanggang.cn/archives/866.html
- 【软件设计】UML类图怎么看
前言 无论使用哪种语言,都离不开面向过程与面向对象两个流派,而类图是面向对象程序设计中至关重要的一种软件表达形式,如何看懂类图,并设计好的软件架构,是我们作为软件工程师必不可少的技能之一. 今天小黑把 ...