题目

描述

题目大意

有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. LeetCode 1108. Defanging an IP Address (IP 地址无效化)

    题目标签:String 题目给了我们一组 ip address,让我们把 . 变成 [.],这题可以用replace,但是这样做的话,好像没意义了.所以还是走一下array,具体看code. Java ...

  2. LeetCode 182. Duplicate Emails (查找重复的电子邮箱)

    题目标签: 题目给了我们一个 email 的table,让我们找到重复的 email. 可以建立 Person a, Person b, 找到两个表格中,emai 相等 但是 id 不同的 email ...

  3. scrt 关闭退格键声音

    options-> session Options -> Terminal -> audio bell (删除勾选) 这样就可以在secureCRT 在出错时不‘滴滴’的响了.

  4. java进行微信h5支付开发

    最近在做微信支付开发用的框架是 srpingMVC mybatis spring 下面是开发流程图 我们只需要开发红色标记的模块就可以了. 具体参数详情可以查看微信开发者文档. 新手第一次写,写的不好 ...

  5. char型指针的初始化问题

    方法一:char *str = “abcd“区别在于你这里赋给str的是一个常量字符串,存储在静态全局区,因此str也成了一个指向常量的指针,不能通过指针对常量内容做任何更改,例如*(ch+2)='y ...

  6. Linux下core文件调试

    1,ulimit -a查看默认参数 2,ulimit -c 1024 设置core文件大小,如果超过1024个blocks,则不会产生core文件 注:tune2fs -l /dev/sda8 输出分 ...

  7. linux centos 装g++安装不了

    今天需要编译一个项目的时候在装g++都装不上, [root@master hadoop]# yum install g++ Loaded plugins: fastestmirror, refresh ...

  8. JQuery AJAX 通过一般处理程序 取列表

    由于上一篇的积累 这一个就简单了 也就是把反回了字符串 显示到table中 $("#btnSearch").click(function () { $.post("Cur ...

  9. 【转】ArcObject与ArcEngine的联系与区别

    在ArcGIS系列产品中,ArcGIS Desktop.ArcGIS Engine和ArcGIS Server都是基于核心组件库ArcObjects搭建的. 所谓ArcObjects,现在一般都是指A ...

  10. 使用mapreduce对日志进行清洗

    网站日志分析项目案例(一)项目介绍:http://www.cnblogs.com/edisonchou/p/4449082.html 网站日志分析项目案例(二)数据清洗:当前页面 网站日志分析项目案例 ...