题目

描述

题目大意

有111到2n2n2n牌,一开始分别给两个人,每人nnn张。

轮流出牌,给出对手出牌的顺序,若自己的牌更大,就记一分。

在中间的某个时刻可以改变游戏规则。

问最大的分数。


思考历程

显然,一定是把大的放分界点左边,把小的放右边。

那可以枚举分界点,两边分别计算就可以了。时间复杂度为O(n2)O(n^2)O(n2),朴素的暴力算法。

接下来我就想有没有什么数据结构可以在枚举分界点的时候维护左右两边的答案。

想不出……

换种简单的思路。

当分界点从左向右移动的时候,左边的答案会增,右边的答案回减。

于是我就天真地想到了三分(如果画成函数图像就是一座山峰),打了个三分上去。

最终沮丧地发现三分是错的,不过还好,可以水到50分。

但实际上这并不是一座山峰,而是连绵起伏,没有什么规律……

莫名其妙地想到模拟退火,可不可以用它来AC这题?


正解

其实这题的解法比较多样,先说题解做法(我AC的做法):

枚举分界点,用数据结构维护左右两边的答案。

如何维护呢?

现在我们只考虑左边,右边的可以分开处理,方法是一模一样的。

我们现在有个大小为2n2n2n的桶,枚举到某个分界点时,这个桶里面有一些点有值,表示这个点存在。这个值还表示它是敌方还是我方。

接下来敌方点要分别在右边匹配我方点,使得匹配数最大。

然后就有一个比较粗暴的思路:可以考虑匹配最近的点。

用个线段树来维护,对于每个节点,记录当前区间内的我方点和敌方点的个数。

合并区间的时候,用左区间的敌方点配对右区间的我方点(尽量配对),加入答案,然后将剩余的加在一起。

这样就使得近的点先匹配到一起。

这个思路是正确的。不妨想想,对于每个敌方点,它们只能匹配在右边的我方点。显然从右到左,它们可以选择的集合的大小是递增的。既然要让匹配数最大,就应当尽量让左边的点在它的集合以内,并且右边的点的集合以外的,这样就不会影响右边的点的选择。所以匹配的最好方式是选择最近的点。

但合并的过程是从小区间到大区间,顺序是乱的,有没有可能出问题呢?出问题的原因在于两个点右边最近的点重合了。如果这样,就其中一个选点,然后另一个会再往后面找。我们不需要关心到底是哪个先选点,因为这样是等价的,我们没有必要关心这些。

有了这个数据结构之后扫一遍,记录答案就可以做出来了,时间复杂度O(nlg⁡n)O(n\lg n)O(nlgn)

还有一个DYP发明的不同算法。主要思路是在桶中枚举,然后用数据结构来修改答案(把答案看成一个序列)。

先把左右两边分开计算(一下以左边为准)。开一个2n2n2n大小的桶。

根据贪心策略,从右到左,然后能匹配就匹配的方案一定是最优的。

从大到小在桶中扫,用一个变量来记录扫到的我方的数量。

遇见一个敌方的点,就开始搞事情:它可能会匹配右边的点。如果右边有点,那就可以匹配,它出现的时间那里的答案加一。如果右边的点,那就不可以匹配,就不加一。

我们维护的是整个答案序列。显然答案序列是递增的。所以可以在数据结构中二分出一个尽量后的地方,使得它的答案小于我方的数量,这就意味着在它后面时间中,那右边的点被占满了。所以就将它出现的时间和二分出来的时间形成的这段区间加一。

做完这些后就记录左边和右边的答案,然后合并。在之前我们可以记录当前的这个答案用到的最右(最左)的我方点位置,表示选取我方点的区间。合并的时候,如果区间重合,那鱼与熊掌不可得兼,减去重合部分就是真正的答案了。


代码

题解方法

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 50010
int n;
int a[N],b[N*2];
bool flag[N*2];
struct Node{
int p,d,w;
} d[N*8];
inline void update1(int k){
int newp=min(d[k<<1].w,d[k<<1|1].d);//两边的最大匹配数
d[k]={d[k<<1].p+d[k<<1|1].p+newp,d[k<<1].d+d[k<<1|1].d-newp,d[k<<1].w+d[k<<1|1].w-newp};
}
inline void add1(int k,int l,int r,int x){
if (l==r){
if (flag[x])
d[k]={0,0,1};
else
d[k]={0,1,0};
return;
}
int mid=l+r>>1;
if (x<=mid)
add1(k<<1,l,mid,x);
else
add1(k<<1|1,mid+1,r,x);
update1(k);
}
inline void update2(int k){
int newp=min(d[k<<1].d,d[k<<1|1].w);
d[k]={d[k<<1].p+d[k<<1|1].p+newp,d[k<<1].d+d[k<<1|1].d-newp,d[k<<1].w+d[k<<1|1].w-newp};
}
inline void add2(int k,int l,int r,int x){
if (l==r){
if (flag[x])
d[k]={0,0,1};
else
d[k]={0,1,0};
return;
}
int mid=l+r>>1;
if (x<=mid)
add2(k<<1,l,mid,x);
else
add2(k<<1|1,mid+1,r,x);
update2(k);
}
int ans1[N],ans2[N];
int main(){
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),flag[a[i]]=1;
for (int i=1,j=0;i<=n*2;++i)
if (!flag[i])
b[++j]=i;
for (int i=0;i<n;++i){
ans1[i]=d[1].p;
add1(1,1,n<<1,a[i+1]);
add1(1,1,n<<1,b[n-i]);
}
ans1[n]=d[1].p;
memset(d,0,sizeof d);
for (int i=n;i>0;--i){
ans2[i]=d[1].p;
add2(1,1,n<<1,a[i]);
add2(1,1,n<<1,b[n-i+1]);
}
ans2[0]=d[1].p;
int ANS=0;
for (int i=0;i<=n;++i)
ANS=max(ANS,ans1[i]+ans2[i]);
printf("%d\n",ANS);
return 0;
}

总结

其实这是一个分治思路,只是用数据结构来动态实现罢了。

所以还是可以往分治方面想……

[JZOJ4684] 【GDOI2017模拟8.11】卡牌游戏的更多相关文章

  1. BZOJ_3191_[JLOI2013]卡牌游戏_概率DP

    BZOJ_3191_[JLOI2013]卡牌游戏_概率DP Description   N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随 ...

  2. 洛谷 P2059 [JLOI2013]卡牌游戏 解题报告

    P2059 [JLOI2013]卡牌游戏 题意 有\(n\)个人玩约瑟夫游戏,有\(m\)张卡,每张卡上有一个正整数,每次庄家有放回的抽一张卡,干掉从庄家起顺时针的第\(k\)个人(计算庄家),干掉的 ...

  3. bzoj千题计划202:bzoj3191: [JLOI2013]卡牌游戏

    http://www.lydsy.com/JudgeOnline/problem.php?id=3191 每个人获胜的概率只与其在排列中与庄家的相对位置有关 dp[i][j] 还剩i个人时,从庄家数第 ...

  4. 【BZOJ3191】【JLOI2013】卡牌游戏 [DP]

    卡牌游戏 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description   N个人坐成一圈玩游戏.一开始我 ...

  5. TCG卡牌游戏研究:《炉石战记:魔兽英雄传》所做的改变

    转自:http://www.gameres.com/665306.html TCG演进史 说到卡牌游戏,大家会联想到什么呢? 是历史悠久的扑克牌.风靡全球的<MTG 魔法风云会>与< ...

  6. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  7. BZOJ 4392 卡牌游戏

    Description 奶牛贝茜是卡牌游戏的狂热爱好者, 但是令人吃惊的, 她缺乏对手. 不幸的是, 任何牧 群里的其他牛都不是好对手. 他们实在是太差了 , 实际上, 他们玩卡牌游戏时会遵循一种完全 ...

  8. [JLOI2013]卡牌游戏 概率DP

    [JLOI2013]卡牌游戏 概率DP 题面 \(dfs\)复杂度爆炸,考虑DP.发现决策时,我们只用关心当前玩家是从庄家数第几个玩家与当前抽到的牌是啥.于是设计状态\(f[i][j]\)表示有\(i ...

  9. [省选联考 2021 A/B 卷] 卡牌游戏

    垃圾福建垫底选手来看看这题. 大家怎么都写带 \(log\) 的. 我来说一个线性做法好了. 那么我们考虑枚举 \(k\) 作为翻转完的最小值. 那么构造出一个满足条件的操作,我们在 \(a_i\) ...

  10. JLOI 2013 卡牌游戏

    问题描述: N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡片上的数字为X,则庄家首先 ...

随机推荐

  1. 安装JDK9之后eclipse无法启动问题解决办法

    操作系统:MAC OS JDK:1.8.0_131 eclipse:官网最新版 安装jdk的过程很简单,从官网下载之后依次点击就可以了,在终端输入:java -version 可以看到安装后的版本信息 ...

  2. struts漏洞处理--老项目struts版本升级遇到的问题

    struts漏洞S2-016被扫描出,要求升级struts版本,查看生产struts版本2.0.12,该漏洞影响版本2.3.15以下,上网搜索,struts2.5以上的要求jdk1.7,由于项目过老, ...

  3. LeetCode 183. Customers Who Never Order (从不订购的客户)

    题目标签: 题目给了我们 Customers 和 Orders 两个表格,让我们找到 从没订购过的客户. 首先从Orders 得到 订购过的CustomerId,然后再去Customers 里找 没有 ...

  4. jquery中typeof的用法

    typeof 可以用来检测给定变量的数据类型,可能的返回值: 'undefined' 'boolean' 'string' 'number' 'object' 'function' var hahah ...

  5. java 8 lambda函数

    1 为什么要引进lambda函数 可以简化编码,将事情更多的交给编译器,让编译器帮我们推断我们写的代码的完整形式. 2 lambda函数的语法 2.1 -> (arg1, arg2) -> ...

  6. java锁分析

    import java.util.concurrent.TimeUnit; class Phone//Phone.java ---> Phone.class Class.forName(); { ...

  7. IOS配置cocos2d-x

    cd /Users/wyc/Desktop/cocos2d-x-3.16/tools/cocos2d-console/bin python cocos.py new HelloWorldDemo -p ...

  8. SpringBoot Redis 订阅发布

    一  配置application.yml spring: redis: jedis: pool: max-active: 10 min-idle: 5 max-idle: 10 max-wait: 2 ...

  9. AOP-面向切面编程-1

    将方法类比成一个积木,哪里需要执行插到哪里 视野角度就是将一个程序比作几条绳子的集合,每个集合是一堆方法的集合,那么把绳子截断,绳子的切面就是一堆方法中一个方法与另一个方法的交界处,将你需要的方法切入 ...

  10. Linux下编译VLC for Android源代码总结

    转:http://blog.chinaunix.net/uid-26611383-id-3678766.html 由于项目需要,需要一个在android平台能够支持RTSP协议的播放器,由于之前没有a ...