BZOJ

洛谷P4363


[Update] 19.2.9

重做了遍,感觉之前写的有点扯= =

首先棋子的放置情况是阶梯状的。

其次,无论已经放棋子的格子上哪些是黑棋子哪些是白棋子,之前得分如何,两人在剩下的棋盘上操作,结束时棋盘的状态也就是得分仍是确定的。

(记忆化不和先前的得分有关系啊,想清楚。)

也就是我们可以记忆化。由上面的分析可知,我们只需要知道每一行现在放了多少个棋子了。事实上这种状态确实不是很多。

搜索的时候是个极大极小搜索,记先手与后手的得分差,先手会最大化这个差,后手会最小化这个差。


之前写的:

每种局面一定是一个阶梯状的样子,这就是一个状态。我们可以将每行有多少个棋子存下来,用一个m+1进制的n位数表示,longlong可以存,于是可以用map记忆化。

转移时枚举放每个棋子即可。


根据组合数学的某些知识,状态数为\(C(n+m,n)\),没问题(搜的时候看一下状态数也是不多的)。

考试时被\(O(nm)\)。。的错误思路限制了,也不知道怎么处理对抗搜索,于是就没写搜索(考前还立flag搜索的省选题不好出不会出吧),mdzz。

用轮廓线DP状压也可以。不过O2 1.5s随便过。

来自\(rqy\)博客


原先的代码:

其实不用\(Unzip()\),因为有那个回溯就够了。。

//6504kb	2932ms
#include <map>
#include <cstdio>
#include <algorithm>
#define INF (0x3f3f3f3f)
typedef long long LL;
const int N=12; int n,m,A[N][N],B[N][N],num[N];
LL End;
std::map<LL,int> mp; bool Unzip(LL sta)
{
int sum=0;
for(int i=n; i; --i) sum+=(num[i]=sta%(m+1)), sta/=(m+1);
return sum&1;
}
LL Zip()
{
LL res=0;
for(int i=1; i<=n; ++i) res=res*(m+1)+num[i];
return res;
}
//void P()
//{
// for(int i=1; i<=n; ++i) printf("%d ",num[i]);
// putchar('\n');putchar('\n');
//}
int DFS(LL sta)
{
if(mp.find(sta)!=mp.end()) return mp[sta];
if(sta==End) return 0;
bool type=Unzip(sta);//0:A:max 1:B:min
int res=type?INF:-INF;
if(num[1]<m)
{
++num[1];
if(type) res=std::min(res,DFS(Zip())-B[1][num[1]]);
else res=std::max(res,DFS(Zip())+A[1][num[1]]);
--num[1];
}
for(int i=2; i<=n; ++i)
if(num[i-1]>num[i])
{
++num[i];
if(type) res=std::min(res,DFS(Zip())-B[i][num[i]]);
else res=std::max(res,DFS(Zip())+A[i][num[i]]);
--num[i];
}
return mp[sta]=res;
} int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j) scanf("%d",&A[i][j]);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j) scanf("%d",&B[i][j]);
for(int i=1; i<=n; ++i) num[i]=m; End=Zip();
DFS(0);
printf("%d",mp[0]); return 0;
}

新写的代码:

然而还是跑的慢。。有更优秀的写法

注意BZOJ上没有c++11(unordered_map)。。

//6504kb	2576ms
#include <map>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
typedef long long LL;
const int N=13,INF=1<<30; int n,m,A[N][N],B[N][N],sta[N];
std::unordered_map<LL,int> f; LL Zip()
{
LL s=0;
for(int i=1; i<=n; ++i) s=s*N+sta[i];
return s;
}
//int Unzip(LL s)
//{
// int cnt=0;
// for(int i=n; i; --i) cnt+=sta[i]=s%N, s/=N;
// return cnt;
//}
int DFS(LL s,int cnt)
{
if(cnt==n*m) return 0;
if(f.count(s)) return f[s];
int res;
if(cnt&1)//B
{
++cnt, res=INF;
for(int i=1; i<=n; ++i)
if(sta[i]<m && sta[i-1]>sta[i])
++sta[i], res=std::min(res,DFS(Zip(),cnt)-B[i][sta[i]]), --sta[i];
}
else//A
{
++cnt, res=-INF;
for(int i=1; i<=n; ++i)
if(sta[i]<m && sta[i-1]>sta[i])
++sta[i], res=std::max(res,DFS(Zip(),cnt)+A[i][sta[i]]), --sta[i];
}
return f[s]=res;
} int main()
{
// freopen("chess.in","r",stdin);
// freopen("chess.out","w",stdout); scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j) scanf("%d",&A[i][j]);
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j) scanf("%d",&B[i][j]);
for(int i=1; i<=n; ++i) sta[i]=0;
sta[0]=INF, sta[1]=1, printf("%d\n",DFS(Zip(),1)+A[1][1]); return 0;
}

BZOJ.5248.[九省联考2018]一双木棋chess(对抗搜索 记忆化)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Description 题库链接 给出一个 \(n\times m\) 的棋盘,棋盘的每个格子有两个权值 \(A,B\) . Alice 和 Bob 轮流操作在棋盘上放棋子,一个格子能放棋子的前提条件 ...

随机推荐

  1. DataGridView刷新数据

    在DataGridView上操作数据之后,无论是增删改都是对数据库进行了操作,而DataGridView这个控件在操作之后是不会变化的,需要重新的去数据库里读取一下数据才行,可以理解为之刷新 Data ...

  2. CSUST 1506 ZZ的计算器 模拟题

    题目描述:实现一个计算器,可以进行任意步的整数以内的加减乘除运算,运算符号只有+.-.*./,求出结果. 解题报告:一个可以说麻烦的模拟题,我们可以这样,输入以字符串的形式输入,然后将输入先做一遍预处 ...

  3. python技巧 is 和 ==

    is 判断变量是否指向同一个对象 == 判断变量引用的对象是否相等 In [2]: a=[1,2] In [3]: b=a In [4]: a == b Out[4]: True In [5]: a ...

  4. python垃圾回收三之标记清除

    #第一组循环引用# a = [1,2] b = [3,4] a.append(b) b.append(a) del a ## #第二组循环引用# c = [4,5] d = [5,6] c.appen ...

  5. C型USB能阻止危险充电器通过USB传播恶意软件

    C型USB能阻止危险充电器通过USB传播恶意软件 C型USB设备(USB Type-C)的新型身份验证协议可以保护用户免受潜在的充电器损坏的风险,这种新型的USB还能减少被恶意软件的风险.基于密码的认 ...

  6. mybatis批量增加与删除——(十五)

    1.首先应该明白,mybatis增删改返回值是int型的影响行数的值 mapper接口 package cn.xm.mapper; import java.util.List; import cn.x ...

  7. House Robber I & II & III

    House Robber You are a professional robber planning to rob houses along a street. Each house has a c ...

  8. iptables-25个常用用法【转】

    本文介绍25个常用的iptables用法.如果你对iptables还不甚了解,可以参考上一篇iptables详细教程:基础.架构.清空规则.追加规则.应用实例,看完这篇文章,你就能明白iptables ...

  9. 公共语言运行库(CLR)开发系列课程(1):Pinvoke 简介 学习笔记

    前言 让拖管代码对象和非托管对象协同工作的过程称为互用性(Interoperability),通常简称为 Interop. P/Invoke在托管代码与非托管代码交互式时产生一个事务(Transiti ...

  10. xunsearch 迅搜初探

    2014年1月2日 19:34:12 [root@localhost bin]# ./php /usr/local/lamp/xunsearch/sdk/php/util/Quest.php demo ...