Description

菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。

落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。

棋盘的每个格子上,都写有两个非负整数,从上到下第i 行中从左到右第j 列的格 子上的两个整数记作Ai,j,,Bi,j 。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的Ai,j​ 之和,牛牛的得分是所有有白棋的格子上的Bi,j 的和。

菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。

Input

输入第一行包含两个正整数n;m,保证n;m <= 10。

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第一个非负整数:其中第i 行中第j 个数表示Ai,j

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第二个非负整数:其中第i 行中第j 个数表示Bi,j​ 。

Output

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

对于所有的测试数据,n;m <= 10 ,Ai,j; Bi,j​ <= 100000。

对于编号为奇数的测试点,保证所有的Bi,j​ = 0 。

Solution

考场上没想出来写的 30 分暴力诶

没想到现在就已经会了

我们定义某一时刻棋盘上的落子情况为当前的状态

定义 s 为初状态,即棋盘上还没有落子

定义 t 为末状态,即棋盘上已经落完子

不难证明,合法的状态小于二十万种

那么先 HASH 一下每个状态,令其唯一对应一个正整数

对于每一个状态,我们可以知道它是从哪些状态转移来的

定义 num[i] 表示 i 状态落了多少子,方便判断当前是该先手还是该后手。

我们 dp 要倒着推,因为如果正着推,有可能出现当前虽然求出了最大价值,但是却不是他们的最优策略的情况。

所以定义 f[i] 表示从状态 i 到末状态 t 先手减后手的最大价值

f[t] 初值为0,f[1] 即为答案

但是怎么求中间状态 f[i] 的值呢?

之前提到过,可以求出 i 状态是由 哪些状态转移来的,假设有一个状态为 j 可以转移到 i

我们用 num 数组求出在状态 j 时是先手下了还是后手下了最后一个棋子,然后分情况讨论

如果是先手:考虑后手的最优策略,显然是想让 f[i] 最小,所以 f[i]=min{f[j]-b[x][y]},x、y 是 j 转移到 i 状态落子的横纵坐标

同理,如果为后手:那么 f[i] 最大的转移方程是 f[i]=max{f[j]+a[x][y]},x、y 的意义跟上面一样

那我们现在就剩最后一个问题了:怎么进行转移呢?

我这里利用了拓扑序进行转移:如果一个状态被所有的后续状态遍历完并求出最优解后,就将其 push 进队列里,让它去转移别人即可。

最坏情况时间复杂度 $O(18万*180万)$ 但是开氧气优化跑的贼快,最慢的点 300ms (反正省选也开 O2 不算作弊)

Code

// By YoungNeal
#include<map>
#include<queue>
#include<cstdio>
#include<cctype>
#define N 400005
#define int long long
#define mod 1000000007
using namespace std;int head[N];
int cnt,s,t;
int n,m,tot;
int qp[N][15];
int f[N],fz[15];
int deg[N],num[N];
int a[15][15],b[15][15];map<int,int> mp;
queue<int> topo;struct Edge{int to,nxt,disa,disb;
}edge[N*10];void add(int x,int y,int z,int p){edge[++cnt].to=y;edge[cnt].nxt=head[x];edge[cnt].disa=z;edge[cnt].disb=p;head[x]=cnt;
}void hsh(int x){int d=0;tot++;for(int i=1;i<=n;i++)d=d*15+fz[i],d%=mod,qp[tot][i]=fz[i];mp[d]=tot;num[tot]=x;if(num[tot]&1) f[tot]=2e18;else f[tot]=-2e18;
}void dfs(int now,int lim,int num){if(now>n){hsh(num);return;}for(int i=0;i<=lim;i++)fz[now]=i,dfs(now+1,i,num+i);
}void _find(){int x=0,y=0;for(int i=1;i<=n;i++) y=y*15+m,y%=mod;s=mp[x],t=mp[y];
}void read(int &x){x=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}signed main(){scanf("%lld%lld",&n,&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]);}dfs(1,m,0);_find();f[t]=0;for(int i=2;i<=tot;i++){for(int j=n;j;j--){if(qp[i][j]==qp[i][j+1]) continue;int x=0;int idx=j,idy=qp[i][j];for(int p=1;p<=n;p++){if(p==j) x=x*15+qp[i][j]-1,x%=mod;else x=x*15+qp[i][p],x%=mod;}if(num[i]&1) add(i,mp[x],a[idx][idy],0);else add(i,mp[x],0,b[idx][idy]);deg[mp[x]]++;}}topo.push(t);while(topo.size()){int u=topo.front();topo.pop();for(int i=head[u];i;i=edge[i].nxt){int to=edge[i].to;if(num[to]&1) f[to]=min(f[to],f[u]-edge[i].disb);else f[to]=max(f[to],f[u]+edge[i].disa);deg[to]--;if(!deg[to]) topo.push(to);}}printf("%lld\n",f[1]);return 0;
}

转载于:https://www.cnblogs.com/YoungNeal/p/8746362.html

[九省联考2018] 一双木棋 chess相关推荐

  1. 「Luogu4363/BZOJ5248」[九省联考2018]一双木棋chess

    「Luogu4363/BZOJ5248」[九省联考2018]一双木棋chess 学校省选模拟居然拿九省联考来考 然而我还是\(too\space young\),搞不懂什么叫最优 让二者的答案最接近可 ...

  2. 【洛谷】【博弈搜索】P4363 [九省联考2018]一双木棋chess

    洛谷 P4363 [九省联考2018]一双木棋chess 题目大意 ◇题目传送门◆ 分析 根据题目所给定的规则,可以发现对于每一行,其下面一行上放的棋子数目不可能多于上面的一行. 所以我们可以将每行上 ...

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

    传送门 分析 我们用0表示向右,1表示向上 于是可以得到一条江棋盘分为两块的线 直接dp即可 代码 #include<iostream> #include<cstdio> #i ...

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

    题目描述 菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子当且仅当这 ...

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

    analysis 这是一道很好的状压dp 这个题首先需要分析出任何一个合法的状态都可以化为从左下角到右上角的一条对角线 这样一来状态就很好表示了: 我们考虑设f[s]表示从状态s出发,最后先手减后手的 ...

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

    题目 题目描述 菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束. 落子的规则是:一个格子可以落子当 ...

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

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

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

    题目描述: 有一个 n∗m的方格, Alice 和 Bob 玩游戏.每次每人可以选择一个格子占领,前提是这个格子未被占领且它左上方的所有格子都已被占领. 第 i行第 j 列的格子若被 Alice 占领 ...

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

    题目描述 菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束. 落子的规则是:一个格子可以落子当且仅当 ...

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

    题目链接:https://www.luogu.org/problemnew/show/P4363 分析: 首先博弈,然后考虑棋盘的规则,因为一个子在落下时它的上面和左面都已经没有空位了,所以棋子的右下 ...

最新文章

  1. LeetCode实战:数组中的第K个最大元素
  2. 【智能语音】ROC-RK3308-CC Amazon Alexa固件发布
  3. opencv图像操作:读取,裁剪,保存,缩放,遍历和读取文件夹图片
  4. perl---dbi,sqlite中文乱码
  5. x265-创建encdata
  6. 主成分分析原理解释(能力工场小马哥)
  7. C#.NET常见问题(FAQ)-如何使用DataGridView跟Excel数据交互
  8. mysql二级索引_mysql 什么是二级索引
  9. 海量数据存储 - 性能瓶颈 - 解决方案
  10. SpringBoot 整合 Editormd(完整版)
  11. 在ROS中创建工作区时出现错误
  12. java根据年份计算生肖
  13. Rasterio入门
  14. 大数据、java、python、区块链、人工智能发展前景
  15. 163net邮箱,对商务办公人士有哪些帮助?
  16. cpld和fpga区别
  17. python爬虫——电影《逐梦演艺圈》影评爬取
  18. 天人合一之毕设——准备阶段——3 HME仿真器
  19. C语言实现小游戏:走迷宫
  20. 软件毕业设计如何降重

热门文章

  1. java矩形类_Java定义矩形类
  2. 7-4 mmh学长与出栈 (20分)
  3. 分布式 id 生成系统 滴滴 Tinyid 快速入门
  4. Apache CXF 拦截器示例
  5. 阶段3 2.Spring_03.Spring的 IOC 和 DI_6 spring中bean的细节之三种创建Bean对象的方式
  6. linux ipc信号量
  7. Rabbitmq消息队列(二) Hello World! 模拟简单发送接收
  8. POJ 3740 Easy Finding (DLX模板)
  9. Windows Store App 中使用Scrollviewer实现缩放功能
  10. UltraNumTextBox【实现所有数字输入的同时,可以控制当控件禁用时ForeColor】