2018 省选 T1 一双木棋
题目描述
菲菲和牛牛在一块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 一双木棋的更多相关文章
- 洛谷 P4363 [九省联考2018]一双木棋chess 解题报告
P4363 [九省联考2018]一双木棋chess 题目描述 菲菲和牛牛在一块\(n\)行\(m\)列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落 ...
- 【BZOJ5248】【九省联考2018】一双木棋(搜索,哈希)
[BZOJ5248][九省联考2018]一双木棋(搜索,哈希) 题面 BZOJ Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何 ...
- [BZOJ5248][九省联考2018]一双木棋(连通性DP,对抗搜索)
5248: [2018多省省队联测]一双木棋 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 43 Solved: 34[Submit][Status ...
- 一双木棋(chess)
一双木棋(chess) 题目描述 菲菲和牛牛在一块 nn 行 mm 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束.落子的规 ...
- 洛谷P4363 一双木棋 chess
洛谷P4363 一双木棋 chess 省选最水的一道题了. 且看我数个月AC一道题...... 具体是这样的:我们发现这个下了棋的地方一定形成一个锯齿形,那么怎么状态压缩呢? 维护轮廓线! 从左下角出 ...
- noi省选 [九省联考2018]一双木棋题解(状压dp)
比浙江简单多了........ 题目转送:https://www.luogu.org/problemnew/show/P4363 分析: 我们注意到n和m都很小,考虑一下状压dp. 显然,棋子摆成的形 ...
- [九省联考2018] 一双木棋 chess
Description 菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手. 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束. 落子的规则是:一个格子可 ...
- B5248 [2018多省省队联测]一双木棋 状压dp
这个题当时划水,得了二十分,现在来整一整. 这个题用状压来压缩边界线,然后通过记忆化搜索进行dp.我们可以观察到,其实每次转移,就是把一个1向左移一位.最后的状态设为0. 这其中还要有一个变量来记录谁 ...
- bzoj 5248: [2018多省省队联测]一双木棋
Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...
随机推荐
- Java 基础之一对象导论
对象导论 1.1 抽象过程 所有编程语言都提供抽象机制.人们所能解决的问题的复杂性直接取决于抽象的类型和质量. 汇编语言是对底层机器的轻微抽象. 我们将问题空间中的元素及其再解空间中的表示称为对象.这 ...
- Linux Namespace : IPC
IPC namespace 用来隔离 System V IPC 对象和 POSIX message queues.其中 System V IPC 对象包含共享内存.信号量和消息队列,笔者在<Sy ...
- Windows10 家庭版 Docker的安装
Docker的安装 1.简介:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中, 然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全 ...
- A. Make a triangle!
题意 给你三条边a,b,c问使得构成三角形,需要增加的最少长度是多少 思路 数学了啦 代码 #include<bits/stdc++.h> using namespace std; #de ...
- Beta项目展示
Team C# 一.团队成员简介 杜正远,队长. 博客地址:http://www.cnblogs.com/kevindu/ 崔强,全职PM. 博客地址:http://www.cnblogs.com/m ...
- 《Linux内核分析》第七周: 可执行程序的装载
LINUX内核分析第七周学习总结--可执行程序的装载 杨舒雯(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/ ...
- linux内核设计第七周——可执行程序的装载
- jiedui
源代码:https://github.com/hanzhaoyan/jieduizuoye/tree/master 功能要求: 该程序用图形界面实现下面功能:用计算机产生一个100以内的随机数,游戏者 ...
- 第三个Sprint冲刺第二天(燃尽图)
- 圆桌的项目Alpha冲刺(团队)
(a) 项目课堂演示 (b) 10篇冲刺随笔 冲刺之一 冲刺之二 冲刺之三 冲刺之四 冲刺之五 冲刺之六 冲刺之七 冲刺之八 冲刺之⑨ 冲刺之十 (c) 1篇测试随笔 测试随笔 (d) 1篇冲刺总结随 ...