[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 ...
随机推荐
- 20151024_004_C#基础知识(C#中的访问修饰符,继承,new关键字,里氏转换,is 和 as,多态,序列化与反序列化)
1:C#中的访问修饰符 public: 公共成员,完全公开,没有访问限制. private: 私有的,只能在当前类的内部访问. protected: 受保护的,只能在当前类的内部以及该类的子类中访问. ...
- Java面向对象的三个特征与含义
封装 1.英文为 encapsulation,实现信息隐藏: 2.把同一类事物的特性归纳到一个类中(属性和行为),隐藏对象的内部实现: 继承 1.英文为 inheritance: 2.继承的过程,是从 ...
- python windows下安装celery调度任务时出错
由于celery 4.0不支持windows系统.所以用命令pip install Celery安装的celery是最新版4.0的不能在windows下运行. 在windows命令窗口运行: cele ...
- MVC 从控制器将数据对象赋值给前端JS对象
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport&quo ...
- linux运维记
nmap 127.0.0.1 命令查看当前服务器对外有多少端口,用于检查漏洞 vim ctrl+z ,jobs,fg 切换控制应用程序 vim 执行命令 #!sh aa.sh执行命令 运维系统监控开源 ...
- ansible安装和配置
一.安装ansible准备 //安装准备 .两台机器 172.7.15.106 172.7.15.111 .设置hostname以及hosts 172.7.15.106 web9.lulu.com 1 ...
- APP运营
产品相关术语 APP:application的简写,即应用. 开发商:也叫CP,即ContentProvider内容提供商. 发行商(运营商):代理CP开发出来的产品. 联运:CP和渠道联合运营产品. ...
- 分割线用CSS样式做出来的效果
一:单个标签实现分隔线:. ; ; line-height: 1px; border-left: 200px solid #ddd; border-right: 200px solid #ddd; t ...
- 关于ueditor在Java中文件上传问题,404问题
问题困扰了两天,部署要求导入到webcontent下,我导入到了整个项目目录下,自己粗心犯错,导致页面访问不到404. 解决了上面的问题,试着进行文件上传,却一直找不到图片: 调出浏览器控制台: 刚开 ...
- servlet为什么要配置web.xml
(1).为Servlet命名: <servlet> <servlet-name>servlet1</servlet-name> <- 这是用于,在serv ...
