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个数表示Bij
n, m ≤ 10 , Aij, Bij ≤ 100000

Output

输出一个整数,表示菲菲的得分减去牛牛的得分的结果。

Sample Input

2 3
2 7 3
9 1 2
3 7 2
2 3 1

Sample Output

2

HINT

Source

[Submit][Status][Discuss]

这套卷子真的是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,对抗搜索)的更多相关文章

  1. P4363 [九省联考2018]一双木棋chess(对抗搜索+记忆化搜索)

    传送门 这对抗搜索是个啥玩意儿…… 首先可以发现每一行的棋子数都不小于下一行,且局面可由每一行的棋子数唯一表示,那么用一个m+1进制数来表示当前局面,用longlong存,开map记忆化搜索 然后时间 ...

  2. 洛谷 P4363 [九省联考2018]一双木棋chess 解题报告

    P4363 [九省联考2018]一双木棋chess 题目描述 菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落 ...

  3. BZOJ5248:[九省联考2018]一双木棋——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=5248 https://www.luogu.org/problemnew/show/P4363#su ...

  4. [九省联考2018]一双木棋chess——搜索+哈希

    题目:bzoj5248 https://www.lydsy.com/JudgeOnline/problem.php?id=5248 洛谷P4363 https://www.luogu.org/prob ...

  5. Luogu4363 [九省联考2018]一双木棋chess 【状压DP】【进制转换】

    题目分析: 首先跑个暴力,求一下有多少种状态,发现只有18xxxx种,然后每个状态有10的转移,所以复杂度大约是200w,然后利用进制转换的技巧求一下每个状态的十进制码就行了. 代码: #includ ...

  6. luogu P4363 [九省联考2018]一双木棋chess

    传送门 对抗搜索都不会,我真是菜死了qwq 首先根据题目条件,可以发现从上到下每一行的棋子数是单调不增的,然后n m都比较小,如果把状态搜出来,可以发现合法状态并不多,所以可以用一个11进制数表示状态 ...

  7. [九省联考2018]一双木棋chess

    题解: 水题吧 首先很显然的是状压或者搜索 考虑一下能不能状压吧 这个东西一定是长成三角形的样子的 所以是可以状压的 相邻两位之间有几个0代表他们差几 这样最多会有2n 然后就可以转移了 由于之前对博 ...

  8. 【题解】Luogu P4363 [九省联考2018]一双木棋chess

    原题传送门 这道题珂以轮廓线dp解决 经过推导,我们珂以发现下一行的棋子比上一行的棋子少(或等于),而且每一行中的棋子都是从左向右依次排列(从头开始,中间没有空隙) 所以每下完一步棋,棋盘的一部分是有 ...

  9. P4363 [九省联考2018]一双木棋chess

    思路 容易发现只能在轮廓线的拐点处落子,所以棋盘的状态可以用一个n+m长度的二进制数表示 转移就是10变成01 代码 #include <cstdio> #include <algo ...

随机推荐

  1. Fire! (双bfs+预处理)

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem ...

  2. 铺砖问题 (状态压缩dp)

    问题描述: 给定m×n个格子,每个格子被染成了黑色或白色.现在要用1×2的砖块覆盖这些格子,要求快于快之间互相不重叠,且覆盖了所有白色的格子(用 . 表示),但不覆盖任意一个黑色的格子(用 x 表示) ...

  3. MongoDB安装配置及使用

    1.安装配置:https://www.cnblogs.com/ymwangel/p/5859453.html 2.使用 from pymongo import MongoClient #连接 conn ...

  4. static class 和 non static class 的区别

    static class non static class 1.用static修饰的是内部类,此时这个 内部类变为静态内部类:对测试有用: 2.内部静态类不需要有指向外部类的引用: 3.静态类只能访问 ...

  5. 【Python学习】解决pandas中打印DataFrame行列显示不全的问题

    在使用pandas的DataFrame打印时,如果表太长或者太宽会自动只给前后一些行列,但有时候因为一些需要,可能想看到所有的行列. 所以只需要加一下的代码就行了. #显示所有列 pd.set_opt ...

  6. 很重要的处理项目url[www]

    http://www.xdowns.com/soft/10/57/2013/Soft_113319.html https://github.com/TricksterGuy/Morphan http: ...

  7. 《gdb调试之基础篇》

    <gdb调试之基础篇> http://blog.csdn.net/miss_acha/article/details/42346543

  8. 【uva11248】网络扩容

    网络流裸题. 求完最大流之后保留残余容量信息,依次将已经加入最小割的弧变成c再跑,记录下即可. #include<bits/stdc++.h> #define N 20005 #defin ...

  9. HDU 6112 今夕何夕 蔡勒公式

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6112题意:中文题目 分析:关键点在与如何计算一个日期是星期几,这个可以通过蔡勒公式来计算.基姆拉尔森计 ...

  10. C 实现一个简易的Http服务器 (二)

    正文 - 直接搞起 C 实现一个简易的Http服务器 很久以前写过一个简易的http服务器, 后面和一个朋友交流, 反思后发现问题不少.在这里简单搞一下. 让其更加简单去表现httpd本质, 弱化协议 ...