QZOI2019 CSP-S模拟赛 T1

错误的贪心导致考场上只有10pts... 看来以后贪心还是需要先证明啊

题目描述

小A和小B在玩一个游戏,他们两个人每人有 $n$ 张牌,每张牌有一个点数,并且在接下来的 $n$ 个回合中每回合他们两人会分别打出手中的一张牌,点数严格更高的一方得一分,然而现在小A通过某种神秘的方法得到了小B的出牌顺序,现在他希望规划自己的出牌顺序使得自己在得分尽可能高的前提下出牌的字典序尽可能大。

题意分析

给出一个数列$b$,给出一个相同长度的数列$a$,要求重新排列$a$,使得最多的$a_i>b_i$,输出字典序最大的一种方案。

思路分析

很自然地想到贪心。

考场上:字典序最大,那倒着最小不就得了(草我是什么sb),10pts

为什么是错的?实际上,如果在线输入在线构造数列$a$,有很多种情况会导致错误。

可以发现,得分与数列的顺序是没有关系的。因此我们完全可以先构造出最大得分,再来构造最大字典序。

具体实现

怎么构造最大得分?可以将$a$和$b$都排序,然后降序遍历$b$,对于当前的$b$,如果当前的$a_i$能得分,就得一分;否则继续遍历。

然后我们构造出一个得分方案。设想,构造出一个得分方案后,我们为了保证得分不变,不能再动已经定下来的这些牌;但我们可以用更大的牌来代替其中的一些牌,这样得分不变。因此我们先构造出一个最小的方案,这样剩下的大牌可以放到不得分的地方,也可以来替代得分的地方,可能性才多。当然,对于构造出的这个方案,我们也要使它在不改变牌组成的情况下,字典序最大。我们暂且将这些没有得分的大牌称为活动牌。

然后我们就开始从前往后遍历$b$序列,若当前元素处于得分方案内,看看有没有活动牌可以替代它,如果有,就用这张牌替代它,并让原来的方案牌成为活动牌;若当前元素不处于得分方案内,我们也可以从方案牌中拿一张最大的牌,当然,如果要拿牌,这张牌一定要可以得分,否则会导致得分减少,同时,因为这里会从方案牌中拿牌,处理得分方案内的牌时,要注意判断当前分配到的方案牌有没有提前被取走。

以上的算法可以通过multiset实现,部分步骤也可以用大根堆替代以降低复杂度,但比较复杂。

下面给出本文代码中用到的一些multiset操作的解释:(可以参考蓝书或网上资料)

multiset<int> s:定义一个有序可重集s
multiset<int>::iterator it:定义一个迭代器it,可以用“*”取消引用
s.insert(x):向s中插入数x
s.erase(it):从s中删除迭代器it指向的元素
s.find(x):返回s中指向x的迭代器
s.begin():返回s中指向第一个元素的迭代器
s.end():返回s中指向最后一个元素后一个地址的迭代器
s.rbegin():返回s中指向最后一个元素的反向迭代器,反向的意思是,若执行++操作,则会向s的前一位移动

考场上忘记函数怎么写怎么办?打开Dev-Cpp目录,搜索头文件set,找到对应的函数即可

//贪心
//先保证得分最大,再保证字典序最大。可以先处理出最大得分的一种方案,然后再调整字典序到最大
//其它有的贪心算法可能会导致字典序或者得分顾此失彼,是错误的
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#define id(i) w[i].id
#define v(i) w[i].v
using namespace std;
const int N=1e5+100;
struct B
{
int id,v;
}w[N];
int n,cnt;
int b[N],ans[N];
multiset<int> s1,s2;
multiset<int>:: iterator it;
bool cmp(B x,B y)
{
return x.v<y.v;
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&b[i]),v(i)=b[i],id(i)=i;
for(int i=1,x;i<=n;i++)
scanf("%d",&x),s1.insert(x),s2.insert(x);
sort(w+1,w+n+1,cmp);
for(int i=n;i;i--)
if(*s2.rbegin()>v(i))
cnt++,s2.erase(s2.find(*s2.rbegin()));//计算最大得分
//先构造出一种出牌最小的方案,这样剩下的活动的牌就大,构造大字典序的空间就大
//本步过后,s1存储的就是方案外的活动牌
//活动牌的定义是,不可以得分的,可以随意移动的牌
for(int i=1;i<=cnt;i++)
{
it=s1.upper_bound(v(i));
ans[id(i)]=*it;s1.erase(s1.find(*it));
}
s2.clear();
for(int i=1;i<=n;i++)
if(ans[i])
s2.insert(ans[i]);//s2存储已构造出的一种方案
for(int i=n;i;i--)
if(ans[i])
{
it=s2.upper_bound(b[i]);
ans[i]=*it,s2.erase(it);
}//将s2的字典序弄到最大
for(int i=1;i<=n;i++)
if(ans[i])
s2.insert(ans[i]);//再存回去
//不改变原方案的牌,为了使字典序最大,可以在活动牌和本来分配给它的牌中找最大的
for(int i=1;i<=n;i++)
if(ans[i] && s2.find(ans[i])!=s2.end())//原方案已分配的部分
s1.insert(ans[i]),s2.erase(s2.find(ans[i])),printf("%d ",*s1.rbegin()),s1.erase(s1.find(*s1.rbegin()));
else//没被分配到的部分
{
if(s2.size() &&(*s1.rbegin()>b[i] || *s2.rbegin()>b[i]))//可以在s2中取个最大的拉过去得分
s1.insert(*s2.rbegin()),s2.erase(s2.find(*s2.rbegin()));
printf("%d ",*s1.rbegin()),s1.erase(s1.find(*s1.rbegin()));
}
return 0;
}

[QZOI2019]Game 题解的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

随机推荐

  1. 在Windows上安装MySQL(转整)

    MySQL安装 在Windows上安装MySQL.首先登录MySQL的官网下载安装包. 选择MySQL installer 这里选择第二个安装包下载即可. 下载完成之后就选择安装那个下载到的文件,基本 ...

  2. PHP linkinfo() 函数

    定义和用法 linkinfo() 函数返回有关一个硬连接的信息. 该函数返回设备 ID,如果失败则返回 FALSE. 语法 linkinfo(path) 参数 描述 path 必需.规定要检查的路径. ...

  3. 数据结构C语言实现----直接插入排序

    直接插入排序(简单插入排序) 给定一个数字串:2 6 7 8 9 3 2 3 4 按从小到大的顺序排列输出 首先把第一个数字放到一个小组里:(2)6 7 8 9 3 2 3 4 让后从第二个数字开始往 ...

  4. x86架构:保护模式下加载并运行用户程序

    本章的代码分3个模块: MBR 引导:加载内核core程序 core:包含内核代码段(从磁盘加载用户程序并重定位).内核数据段(存放api名称.临时缓冲.字符串等).API段(供用户程序调用) 用户程 ...

  5. day12. 闭包

    一.概念 """ 如果内函数使用了外函数的局部变量, 并且外函数把内函数返回出来的过程,叫做闭包 里面的内函数是闭包函数 """ 二.基本语 ...

  6. 如何在Ubuntu18.04里面添加中文输入法

    1. 安装语言包 System Settings–>Region&language->Manage installed languages–>Install/Remove L ...

  7. 【JZOJ4725】质数序列 题解(数学)

    题目大意:质数序列是指这个序列中任意两个数的和均为质数.先给出一个序列${a_{n}}$,从中取出元素构成最长质数序列,问其长度并输出序列.若长度相同则求和最大的序列.保证答案唯一. -------- ...

  8. 改变对象的字符串显示__str__repr

    改变对象的字符串显示 # l=list('hello') # # print(l) # file=open('test.txt','w') # print(file) class Foo: def _ ...

  9. Java web 小测验

    题目要求: 1登录账号:要求由6到12位字母.数字.下划线组成,只有字母可以开头:(1分) 2登录密码:要求显示“• ”或“*”表示输入位数,密码要求八位以上字母.数字组成.(1分) 3性别:要求用单 ...

  10. Python中匿名函数与内置高阶函数详解

    大家好,从今天起早起Python将持续更新由小甜同学从 初学者的角度 学习Python的笔记,其特点就是全文大多由 新手易理解 的 代码与注释及动态演示 .刚入门的读者千万不要错过! 很多人学习pyt ...