题目描述

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

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

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

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

从文件chess.in 中读入数据。

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

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

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

输出格式:

输出到文件chess.out 中。

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

分析:

首先,左边、上边所有格子和左边格子、上边格子都填满其实是一样的。

可以通过n/m <=10 想到状压dp

一般大家用的状压dp都是维护之前的几行从左数有几个旗子已经下过。

因为发现,棋盘上下过的地方总是右上角的一个阶梯形状,剩下的总是一个右下角的部分。所以高级的做法是:维护已下过的部分和没有下过的部分的分界线的状态。(1表示横,0表示竖) 状态查看时,从末位向前看,从棋盘左下角划线。

例如样例中,11100是初始状态,00111是最终的状态。

我们可以dfs预处理出所有的状态,C(20,10)种合法状态。接着,我么可以预处理出每个状态的转弯处(0,1交汇处)通过这个转弯处可以下一个棋子,从而转移到下一个状态。

需要注意的是最后work的方法。(又卡了一天)

不能用递推!因为之前的局部最优策略下的最优解可能不是最终局面下的形式。而由于后面的局面“最优解”是通过这个局部策略转移过来的,导致全部错误。

例如:

3 3

9 4 3

6 7 6

4 5 9

0 0 0

0 0 0

0 0 0

错误输出是 29 正解 32

错误的过程是:

9 4 3

0 0 0

4 0 9

正解:

9 0 3

0 7 0

4 0 9

正解中,下棋时会先填满左边一列,而错误解法则是随机的一块中的当前最优解来更新。左上角三个数,错误解法中这就是局部最优解,但是与正解相差甚远。

所以考虑设f[i]表示i状态下,剩下的格子下法中最优解的答案(是一个a-b的差值)

当该a下时,初值f[i]=-inf f[i]=max(f[i],dfs(to,who^1)+a[x][y]); 当该b下时,初值f[i]=inf; f[i]=min(f[i],dfs(to,who^1)-b[x][y]);

加上记忆化搜索即可。(在这里剪掉的是一种局面可能由多种局面下出来的情况,避免再往后推) 100行代码,不开O2照样水过。

总结: 1.应用的算法:状压dp与记忆化搜索结合。 2.注意转移时的方式和顺序。保证最优子结构。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=;
const int M=+;
const int inf=0x3f3f3f3f;
int f[M];
int ret[];
struct node{
int wei[*N][];
int tur;
int zhi;
int size;
}g[M];
int n,m;
int a[N][N],b[N][N];
int cnt=;
int vis[M];
void dfs(int x,int sum,int num1,int num0)
{
if(x==(n+m)+)
{
if(num1==m&&num0==n)
{
g[++cnt].zhi=sum;
}
return;
}
dfs(x+,sum+(<<x-),num1+,num0);
dfs(x+,sum,num1,num0+);
}//0 up 1 right
int dfs2(int hao,int who)
{
if(vis[hao]) return f[hao];
int i=hao;
vis[hao]=;
if(hao==cnt) return ;
if(who&) f[hao]=inf;
else f[hao]=-inf;
for(int i=;i<=g[hao].tur;i++)
{
int h=g[hao].wei[i][];
int l=g[hao].wei[i][];
int x=g[hao].wei[i][];
int y=g[hao].wei[i][];
int to=ret[g[hao].zhi+(<<x-)-(<<y-)];
if(who&) f[hao]=min(f[hao],dfs2(to,-who)-b[h][l]);
else f[hao]=max(f[hao],dfs2(to,-who)+a[h][l]);
}
return f[hao];
}
bool cmp(node a,node b)
{
if(a.size!=b.size) return a.size<b.size;
return a.zhi>b.zhi;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&a[i][j]);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&b[i][j]);
dfs(,,,);
for(int i=;i<=cnt;i++)
{
int t=g[i].zhi;
int s=;
int last;
int num[];
memset(num,,sizeof num);
while(s!=(n+m))
{
s++;
if(t&) g[i].size+=n-num[];
if(s==) last=(t&);
else{
if(last==&&((t&)==))
{
g[i].wei[++g[i].tur][]=n-num[]+;
g[i].wei[g[i].tur][]=num[]+;
g[i].wei[g[i].tur][]=s-;
g[i].wei[g[i].tur][]=s;
}
last=(t&);
}
num[t&]++;
t>>=;
}
}
sort(g+,g+cnt+,cmp);
for(int i=;i<=cnt;i++)
ret[g[i].zhi]=i;
printf("%d",dfs2(,));
return ;

}

2018 省选 T1 一双木棋的更多相关文章

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

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

  2. 【BZOJ5248】【九省联考2018】一双木棋(搜索,哈希)

    [BZOJ5248][九省联考2018]一双木棋(搜索,哈希) 题面 BZOJ Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何 ...

  3. [BZOJ5248][九省联考2018]一双木棋(连通性DP,对抗搜索)

    5248: [2018多省省队联测]一双木棋 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 43  Solved: 34[Submit][Status ...

  4. 一双木棋(chess)

    一双木棋(chess) 题目描述 菲菲和牛牛在一块 nn 行 mm 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束.落子的规 ...

  5. 洛谷P4363 一双木棋 chess

    洛谷P4363 一双木棋 chess 省选最水的一道题了. 且看我数个月AC一道题...... 具体是这样的:我们发现这个下了棋的地方一定形成一个锯齿形,那么怎么状态压缩呢? 维护轮廓线! 从左下角出 ...

  6. noi省选 [九省联考2018]一双木棋题解(状压dp)

    比浙江简单多了........ 题目转送:https://www.luogu.org/problemnew/show/P4363 分析: 我们注意到n和m都很小,考虑一下状压dp. 显然,棋子摆成的形 ...

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

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

  8. B5248 [2018多省省队联测]一双木棋 状压dp

    这个题当时划水,得了二十分,现在来整一整. 这个题用状压来压缩边界线,然后通过记忆化搜索进行dp.我们可以观察到,其实每次转移,就是把一个1向左移一位.最后的状态设为0. 这其中还要有一个变量来记录谁 ...

  9. bzoj 5248: [2018多省省队联测]一双木棋

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

随机推荐

  1. WPF制作带明细的环形图表

    效果 明细用Popup实现的,录gif时,Popup显示不出来,不知道为什么,所以静态图凑合看吧 大体思路 图表使用Arc+Popup实现 图表分为两部分,一是环形部分,一是标注的明细部分. 环形部分 ...

  2. RocketMQ 简单梳理 及 集群部署笔记

    一.RocketMQ 基础知识介绍Apache RocketMQ是阿里开源的一款高性能.高吞吐量.队列模型的消息中间件的分布式消息中间件. 上图是一个典型的消息中间件收发消息的模型,RocketMQ也 ...

  3. python-lambda用法

    前言: lambda函数也叫匿名函数,即,函数没有具体的名称. 一.基础 lambda语句构建的其实是一个函数对象.匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果 ...

  4. 1023 C. Bracket Subsequence

    传送门 [http://codeforces.com/contest/1023/problem/C] 题意 n字符串长度,k要求的字符串的长度,字符串只包含'('和')',而且这两种的数量相等,要求的 ...

  5. 领跑衫获奖感言 & 课程总结

    很荣幸在最后一次课获得了黄色领跑衫.在此,我要感谢教师杨贵福,感谢<构建之法>的作者邹欣老师和出版人周筠老师,感谢“耐撕”团队的队员们. 作为旁听生,最后一堂课,有些不舍.不多说,先上图, ...

  6. 《蹭课神器》Beta版使用说明

    相比 Alpha 版,我对主界面进行了优化,使主界面更加简洁 同时数据库增加了一个表,里面存放的是课程的详细信息

  7. js异步回调

    简单理解:js是单线程的,Ajax请求远程数据.IO等会很耗时,引起堵塞可能会引起反应时间太长页面失去反应. 回调:A函数作为一个参数传给B函数,执行完B后再执行A: 同步回调: function A ...

  8. eclipse插件wordwrap

    一行代码很长,浏览不方便,安装wordwrap可以自动折行. help->install new software-,在Workwith输入wordwrap - http://ahtik.com ...

  9. Mordern Effective C++ --auto

    5. 优先使用auto而非显示类型声明 在C++之中,使用auto关键字声明类型可以将程序员从输入繁琐的类型中解放出来,编译器会自动推导出变量的实际类型. template<typename I ...

  10. WebAssembly是什么?

    现在的JavaScript代码要进行性能优化,通常使用一些常规手段,如:延迟执行.预处理.setTimeout等异步方式避免处理主线程,高大上一点的会使用WebWorker.即使对于WebWorker ...