牛客练习赛46----D-华华陪奕奕打怪兽

2024-03-17 12:10

本文主要是介绍牛客练习赛46----D-华华陪奕奕打怪兽,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先发出题目链接:
链接:https://ac.nowcoder.com/acm/contest/894/D
来源:牛客网
涉及:二分


题目如下:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
这一类的题目有一个主要的特点,就是答案的范围和输入的数据的范围具有很强的映射性,由于答案的范围已知,所以这种题目的做法就是二分答案,然后判断答案是否符合题目的要求和输入的数据特点,从而二分得到最合适的答案


为了二分答案ans,首先要知道ans的范围。

ans为题目要求的最小时间,即ans的值一定大于1,由于题目中给了a[i]=a[n-i]的条件,故无限序列a是一个循环序列,故每秒第j次攻击需要的精力数(a[i]*b[j])组成的二维数组平面也是一个循环的平面

举个例子,假如
a={2,4,5,6,2,4,5,6,2,4,5,6…}
b={1,2,3}
那么组成的每秒第j次攻击需要的精力数(a[i]*b[j])组成的平面为

精力数a[1]=2a[2]=4a[3]=5a[4]=6a[5]=2a[6]=4a[7]=5a[8]=6a[9]=2
b[1]=1245624 562
b[2]=2481012 4 8 10124
b[3]=36121518 61215186

称这个平面每一个循环节叫做一个循环面每一个循环面最少可能有一个a[i]*b[j](一次攻击)满足条件,由于需要c次攻击才算胜利,故ans的最大值不超过 n ∗ c n*c nc


对于每次二分得到的ans,表示在ans秒内完成了c次攻击,而每一秒可以完成多次攻击,我们可以把前ans秒内的a[i]*b[1](i<=ans)全部放入优先队列排序进行贪心的选取最小消耗的精力的某次攻击,如果选取了某个a[i]*b[1](即这一秒内攻击了一次),则将对应的a[i]*b[2]放入队列(表示这一秒可以进行下次攻击)。

但是注意,ans可能很大,把全部的a[i]*b[1](i<=ans)放入到队列里面可能会超时,由于a序列本来就是循环的,我们只需将一个循环的所有a[i]*b[1](i<=n)放入优先队列,然后每次选取到最小的a[k]*b[1]后,就判断一下这一个a[k]*b[1]在前ans秒内循环了几次,然后判断是否需要将前ans秒内所有的这次攻击(a[k]*b[1])全部选取(表示还需要攻击这么多次),然后将对应的a[i]*b[2]放入队列。

如果还需要攻击的次数timeq小于a[k][i]在前ans秒内循环的次数timep,则只需选取任意timeq个a[k]*b[1]即可。

注意剪枝:如果某次攻击a[i]*b[j]在前ans次内循环了0次,则可以直接重新在优先队列内进行选取,不需要将对应的a[i]*b[j+1]放入队列(反正循环次数都是0)。

PS:每次选择了一个a[i]*b[j]后要判断当前已经消耗了的精力是否大于等于初始精力w
1.如果攻击次数小于c但是消耗精力大于等于w表示ans太小了
2.如果已经消耗的精力小于w但攻击次数已经到达c表示ans太大了
3.当ans的左边界f大于右边界l时,有边界l+1就是答案
3.如果当左边界f大于右边界l时,且右边界l= c ∗ n c*n cn,则无解


举个例子:
比如n=3,c=2,w=3
a={4,5,1}
b={1,2}

1.开始左边界f=1,右边界l=c*n=6,ans=(6+1)/2=3
前3秒精力值消耗表如下(红色代表优先队列中的元素,加粗表示优先队列第一项,黑色表示未被选择,灰色表示已经被选择,括号内表示前ans秒a[i]*b[j]重复的次数):

精力数a[1]=4a[2]=5a[3]=1
b[1]=14(1)5(1)1(1)
b[2]=28(1)10(1)2(1)

选取全部的 a [ 3 ] ∗ b [ 1 ] a[3]*b[1] a[3]b[1],存入 a [ 3 ] ∗ b [ 2 ] a[3]*b[2] a[3]b[2]
此时精力消耗数sumw=1*1=1,攻击次数sumc=1(sumc<c,sumw<w,继续选择)

精力数a[1]=4a[2]=5a[3]=1
b[1]=14(1)5(1)1(1)
b[2]=28(1)10(1)2(1)

选取全部的 a [ 3 ] ∗ b [ 2 ] a[3]*b[2] a[3]b[2]
此时精力消耗数sumw=1+2*1=3,攻击次数sumc=2(sumw=w,停止选择)
此时ans=3不满足条件,表示ans可能太小了

2.左边界f=ans+1=4,右边界l=6,ans=(6+4)/2=5
前5秒精力值消耗表如下(红色代表优先队列中的元素,加粗表示优先队列第一项,黑色表示未被选择,灰色表示已经被选择,括号内表示前ans秒a[i]*b[j]重复的次数):

精力数a[1]=4a[2]=5a[3]=1
b[1]=14(2)5(2)1(1)
b[2]=28(2)10(2)2(1)

选取全部的 a [ 3 ] ∗ b [ 1 ] a[3]*b[1] a[3]b[1],存入 a [ 3 ] ∗ b [ 2 ] a[3]*b[2] a[3]b[2]
此时精力消耗数sumw=1*1=1,攻击次数sumc=1(sumc<c,sumw<w,继续选择)

精力数a[1]=4a[2]=5a[3]=1
b[1]=14(2)5(2)1(1)
b[2]=28(2)10(2)2(1)

选取全部的 a [ 3 ] ∗ b [ 2 ] a[3]*b[2] a[3]b[2]
此时精力消耗数sumw=1+2*1=3,攻击次数sumc=2(sumw=w,停止选择)
此时ans=5不满足条件,表示ans可能太小了

3.左边界f=ans+1=6,右边界l=6,ans=(6+6)/2=6
前6秒精力值消耗表如下(红色代表优先队列中的元素,加粗表示优先队列第一项,黑色表示未被选择,灰色表示已经被选择,括号内表示前ans秒a[i]*b[j]重复的次数):

精力数a[1]=4a[2]=5a[3]=1
b[1]=14(2)5(2)1(2)
b[2]=28(2)10(2)2(2)

选取全部的 a [ 3 ] ∗ b [ 1 ] a[3]*b[1] a[3]b[1],存入 a [ 3 ] ∗ b [ 2 ] a[3]*b[2] a[3]b[2]
此时精力消耗数sumw=1*2=2,攻击次数sumc=2(sumc=c,停止选择)
此时ans=6满足条件,表示ans可能太大了

4.左边界f=6,右边界l=ans-1=5
由于f>l,且l不等于 c ∗ n c*n cn,故答案ans=l+1=6

ps:题目中的例子刚好每次都选取了所有重复部分,还有可能只用选取一部分的重复部分


代码如下:

#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
typedef struct data{ll num;//num是消耗的精力值(a[i]*b[j])ll x;//x是第x秒ll y;//y是这一秒的第y次攻击data(ll a,ll b,ll c){num=a;x=b;y=c;}
}data;
struct cmp{bool operator()(const data p1,const data p2){return p1.num>p2.num;}
};
priority_queue<data,vector<data>,cmp> que,r_que;//优先队列以num从小到大为主键
ll n,c,w;//题目所给变量
int a[100005],b[100005];//题目所给变量数组
int main(){scanf("%lld%lld%lld",&n,&c,&w);int i;for(i=1;i<=n;i++)	scanf("%d",&a[i]);for(i=1;i<=c;i++)	scanf("%d",&b[i]);for(i=1;i<=n;i++)	r_que.push(data(a[i]*b[1],i,1));//r_que对=队列用来每次初始化que队列ll f=1,l=n*c,ans;//开始二分while(f<=l){que=r_que;//初始que队列ans=(f+l)/2;//timep指前ans秒所有num的最小重复次数//timeq+timep指前ans秒num的最大重复次数ll timep=ans/n,timeq=ans%n;//这一步用来计算当前ans所对应的最大和最小重复次数ll sumw=0,sumc=0;//sumw存已经消耗了的精力值,sumc存已经攻击的次数	while(1){data p=que.top();que.pop();ll time=(p.x<=timeq?timep+1:timep);//time存当前ans秒的当前num重复次数if(time==0)	continue;//重复次数为0,直接剪枝if(c-sumc>=time){//可以选择前ans秒的所有这次攻击(攻击是循环重复的)sumc+=time;sumw+=(p.num*time);if(p.y+1<=c)	que.push(data(a[p.x]*b[p.y+1],p.x,p.y+1));//将这一秒的下次攻击放入队列,供选择}else{//次数到达了c次,跳出选择sumw+=(c-sumc)*p.num;break;	}if(sumw>=w)	break;//如果消耗的精力大于等于初始精力值,已经不符合条件,跳出选择}if(sumw>=w)	f=ans+1;//如果消耗的精力大于等于初始精力值,表示ans太小了else	l=ans-1;//如果消耗的精力小于初始精力值,表示ans可能太大了}if(l==n*c && f>l)	return	!(cout<<"-1");//如果f>l,表示ans最终还是不符合条件,不存在ans值else	return !(cout<<l+1);
}

这篇关于牛客练习赛46----D-华华陪奕奕打怪兽的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/818916

相关文章

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II

代码随想录刷题day25丨491.递增子序列 ,46.全排列 ,47.全排列 II 1.题目 1.1递增子序列 题目链接:491. 非递减子序列 - 力扣(LeetCode) 视频讲解:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili 文档讲解:https://programmercarl.com/0491.%E9%80%92%E

牛客小白月赛100(A,B,C,D,E,F三元环计数)

比赛链接 官方讲解 这场比较简单,ABC都很签到,D是个不太裸需要预处理的 B F S BFS BFS 搜索,E是调和级数暴力枚举,F是三元环计数。三元环考的比较少,没见过可能会偏难。 A ACM中的A题 思路: 就是枚举每个边变成原来的两倍,然后看看两短边之和是否大于第三边即可。 不能只给最短边乘 2 2 2,比如 1 4 8 这组数据,也不能只给第二短边乘 2 2 2,比

笔试强训,[NOIP2002普及组]过河卒牛客.游游的水果大礼包牛客.买卖股票的最好时机(二)二叉树非递归前序遍历

目录 [NOIP2002普及组]过河卒 牛客.游游的水果大礼包 牛客.买卖股票的最好时机(二) 二叉树非递归前序遍历 [NOIP2002普及组]过河卒 题里面给的提示很有用,那个马的关系,后面就注意,dp需要作为long的类型。 import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息publ

天然药物化学史话:“四大光谱”在天然产物结构鉴定中的应用-文献精读46

天然药物化学史话:“四大光谱”在天然产物结构鉴定中的应用,天然产物化学及其生物合成必备基础知识~ 摘要 天然产物化学研究在药物研发中起着非常重要的作用,结构研究又是天然产物化学研究中最重要的工作之一。在天然药物化学史话系列文章的基础上,对在天然产物结构研究中起绝对主导作用的“四大光谱”分析技术,即红外光谱、紫外光谱、质谱、核磁共振波谱在天然产物结构鉴定中的应用历史进行回顾与总结,并对其发展

【笔记-流程记录】从零开始做一个人形怪兽(建模阶段)

大型 1.第一步还是找素模,打开材质球,吸管点一下,就会出现素模的贴图,一共有四个 比如,点进去第一个,再点漫反射,再点psd就会得到相应的贴图 2.然后我们依然是面片然后插入参考图 如果透视窗口啥都没有,按g也不显示线框。那按下z(居中视图),然后再试一下按G显示栅格。 3.导入素模,重置变换 注释:重置变换是一个非常有用的功能,主要用于将对象的变换属性(位置、旋

每日OJ_牛客_求和(递归深搜)

目录 牛客_求和(递归深搜) 解析代码 牛客_求和(递归深搜) 求和_好未来笔试题_牛客网 解析代码         递归中每次累加一个新的数,如果累加和大于等于目标,结束递归。此时如果累加和正好等于目标,则打印组合。向上回退搜索其它组合。此题本身就是一个搜索的过程,找到所有的组合。 #include <iostream>#include <cmath>#in

牛客《剑指Offer》 -- 数值的整数次方

题目描述 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。 思路 特别注意负数的情况,出现负数,将其转化为正数然后求倒数。 class Solution {public:double Power(double base, int exponent) {double total = 1;bool flag = false

牛客网《剑指Offer》 二进制中1的个数

题目描述 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 思路 负数用补码,其实就是求一个数据在计算机中是存储是怎么样子的。用位运算,就能很好实现。 class Solution {public:int NumberOf1(int n) {int count = 0;int flag = 1;while (flag != 0) {if ((n & f