蓝桥杯 试题 算法训练 礼物 C++ 详解
题目:
JiaoShou在爱琳大陆的旅行完毕,即将回家,为了纪念这次旅行,他决定带回一些礼物给好朋友。
在走出了怪物森林以后,JiaoShou看到了排成一排的N个石子。
这些石子很漂亮,JiaoShou决定以此为礼物。
但是这N个石子被施加了一种特殊的魔法。
如果要取走石子,必须按照以下的规则去取。
每次必须取连续的2*K个石子,并且满足前K个石子的重量和小于等于S,后K个石子的重量和小于等于S。
由于时间紧迫,Jiaoshou只能取一次。
现在JiaoShou找到了聪明的你,问他最多可以带走多少个石子。
输入格式
第一行两个整数N、S。
第二行N个整数,用空格隔开,表示每个石子的重量。
输出格式
第一行输出一个数表示JiaoShou最多能取走多少个石子。
样列输入
8 3
1 1 1 1 1 1 1 1
样列输出
6
数据规模和约定
对于20%的数据:N<=1000
对于70%的数据:N<=100,000
对于100%的数据:N<=1000,000,S<=10^12,每个石子的重量小于等于10^9,且非负
前言:
还是一样,网上的方法(二分查找),又是不怎么解释过程。但是数据量太大,没空去慢慢测试数据,干脆自己想一个方法,也方便详细讲解解题过程。
开始:(已明确题意)
(也不知道是积累的做题经验,还是灵光一现,我想到:)
既然是要满足:前 K 个 <= S , 后 K 个也 <= S . 那不就类似分区嘛。按照S进行分区,找出两个相邻(或者部分重叠)的两个区间,其长度之和即为最多可拿的石子数量(其实这句话不太对,后面会慢慢改进的)。
还是我最喜欢的举例(例子):7 4 1 0 8 5 2 9 6 3,那么显然对它分区的结果为:(图解)
分区方式:7 对应 1 代表 若从 7 开始选,至多选 1 个(满足 <= S) 7 <= 10
4 对应 3 代表 若从 4 开始选,至多选 3 个(满足 <= S) 4 + 1 + 0 <= 10
以此类推……
【分区数组所存放的是:对应原始数组(相同下标),从当前位置往后选,不超过 S 的情况下,最多可以选多少个石子】
得到所有的分区情况,那就开始遍历,找最长相邻区间即可。
1.1 先挑最简单的情况:从0开始选,分区结果为2,意味着前S能选2个、选0之后,"去0到的后2位",到5,分区结果为2,意味着(后S)能选2个。综合得:从0开始选,前K后K不超过S情况下,可以最多选 2 + 2 = 4个。(满足:前K后K不超过S,前K == 后K)
【看到这里,停下来,理解上面的操作:(关键)"去0到的后2位",之后再往下看,这只是第一步】
然后遍历过程不断更新最大值(当然奇数的话,要-1为偶数)……
咔咔一顿敲,提交上去:正确率10%,用时1.0XXs(超时)。
超时的原因,时间复杂度:输入O(N),分区O(N^2),遍历O(N)。综合:O(N^2)这在蓝桥杯这样的比赛显然是无法忍受的,效率太低了,于是我又优化了一下,可以参考代码对比一下:
//输入操作 + 优化前的分区操作for (int i = 1; i <= N; i++) cin >> ii[i];for (int i = 1; i <= N; i++){int sum = 1; int j = i + 1; long long add = ii[i];while (add + ii[j] <= S && j <= N){add += ii[j]; sum++; j++;}jj[i] = sum;}//输入操作 + 优化后的分区操作int left = 1;long long add = 0;for (int i = 1; i <= N; i++){cin >> aa[i]; add += aa[i];while (add > S){bb[left] = i - left; add -= aa[left++];}}while (left <= N){bb[left] = N - left + 1; left++;}
这下把分区优化到O(N)的时间复杂度。
时间缩短了,但正确率还是10%,看来还是逻辑有疏漏。
1.2 接下来看其他情况:
从7开始,分区为1,前S选1个、选7之后,"去7到的后1位",到4,分区为3,(后S)能选3个。综合得:从7开始选,前K后K不超过S情况下,可以最多选 1 + 3 = 4个。(满足:前K后K不超过S,但是不满足:前K == 后K)
这时候就要改进了。如果选7,为了满足前K不超过S,那么只能选1个,为了前K == 后K,后面也只能选1个,总共2个。那如果不选7,哪怕只在(4 1 0)中选,也最多能选2个。(看着我个人都觉得好绕,但其实并不复杂,应该是我个人表述能力不行……)
所以,当前S的K小于后S的K,比较(前S的K * 2)和(后S的K)哪个更大,大的即为结果。
同理,当前S的K大于后S的K,比较(前S的K)和(后S的K * 2)哪个更大,大的即为结果。
接下来,还是举几个例子吧。
(石子数量)N = 7,(限制重量)S = 10,(石子重量)2 2 2 2 2 5 5,分区为:5 4 3 3 2 2 1
(在纸上推导正确)结果为:4,改进前:(5 + 2 = 7, 7 - 1 = )6,改进后:(5 > 2 * 2, 5 - 1 = )4
(石子数量)N = 7,(限制重量)S = 10,(石子重量)5 5 2 2 2 2 2,分区为:2 3 5 4 3 2 1
(在纸上推导正确)结果为:4,改进前:(2 + 5 = 7, 7 - 1 = )6,改进后:(2 * 2 < 5, 5 - 1 = )4
N = 10,S = 12,2 2 2 2 2 2 3 3 3 3 ,分区为:6 5 5 5 4 4 4 3 2 1
(在纸上推导正确)结果为:8,改进前:(6 + 4 = )10,改进后:(6 < 2 * 4, 2 * 4 = )8
……
好了,完善到这里,已经有90%的正确率了。
还剩最后10%,请看这个案例:N = 11 , S = 8 , 1 1 1 1 1 1 1 1 2 3 3 , (分区)8 7 7 6 5 5 4 3 3 2 1
(在纸上推导正确)结果为:10,改进后:(8 > 2 * 3)8,错误。在纸上推导的我们很容易得出,答案10是由第二个(分区中的)5得出的。因为它前5位是8,意味着它前面第五个数,如果选了,则可以选8个,自然囊括了它在内。而选了它,分区为5,最多可选5个。所以它本身5个,加上前面8内的5个,加起来10个也就是最优解。(说起来挺复杂的,但其实动手在纸上写一写,其实很容易理解)
所以,综合得:(伪代码?)
1、定义数组 aa[1000001], 数组 bb[1000001];2、输入数据(到 aa),同时进行分区(到 bb);3、遍历( bb),从 bb[i], bb[i + bb[i]], bb[i - bb[i]] 中找出最大的……
【遍历到数组的某个元素,看三个:元素本身,该元素前"元素值"个,该元素后"元素值"个。当中最大的为结果】
还有分区,优化后的分区(自行理解)还是比较有趣的,差不多,就这样吧。
附上代码:
#include<iostream>
using namespace std;//温馨提示:过大的数据要放在函数体外
int aa[1000001] = { 0 };
int bb[1000001] = { 0 };//取最大
int Max(int a, int b, int c = -1)
{int max = (a > b) ? a : b;return (max > c) ? max : c;
}//取最小
int Min(int a, int b)
{return (a < b) ? a : b;
}int main()
{int N; cin >> N;long long S; cin >> S;int left = 1;long long add = 0;for (int i = 1; i <= N; i++){cin >> aa[i];add += aa[i];//while循环就是执行"分区"的操作了。while (add > S){bb[left] = i - left;add -= aa[left++];}}//也是"分区"的操作(收尾)while (left <= N){bb[left] = N - left + 1;left++;}//测试:检查分区情况//for (int i = 1; i <= N; i++)//{// cout << bb[i] << " ";//}//cout << endl;/*注意:下面这段for循环内的注释,写得不好,尽量自行理解,不要被我的注释限制思路*/int MaxSum = 0;for (int i = 1; i <= N; i++){//当前遍历到的长度int Len01 = bb[i];//选了Len01之后,进到下一S分区的长度int Len02 = 0;if (i + bb[i] <= N) Len02 = bb[i + bb[i]];//以Len01长度分区的上一段int Len03 = 0;if (i - bb[i] >= 0 && bb[i] <= bb[i - bb[i]]) Len03 = bb[i] * 2;int MaxLen = Max(Len01, Len02, Len03);int MinLen = Min(Len01, Len02);MaxLen = Max(MaxLen, MinLen * 2);//更新最优解MaxSum = Max(MaxSum, MaxLen);}//奇数则化为偶数,位运算妙用cout << (((MaxSum & 1) == 1) ? MaxSum - 1 : MaxSum);return 0;
}
结束:
并不是很复杂,多在纸上推导几遍即可。感觉表述得不好,希望理解吧。
(文章写得很急,哪里不懂的,私信或者评论问我,有空都会回复)
蓝桥杯 试题 算法训练 礼物 C++ 详解相关推荐
- 蓝桥杯 试题 算法训练 跳马 C++ 详解
题目: 问题描述 一个8×8的棋盘上有一个马初始位置为(a,b),他想跳到(c,d),问是否可以?如果可以,最少要跳几步? 输入格式 一行四个数字a,b,c,d. 输出格式 如果跳不到,输出-1:否 ...
- 蓝桥杯 试题 算法训练 印章 C++ 详解
题目: 共有n种图案的印章,每种图案的出现概率相同.小A买了m张印章,求小A集齐n种印章的概率. 前言: 建议先看以下两篇文章:(我也是看了之后才懂得) https://blog.csdn.net/o ...
- 蓝桥杯 试题 算法训练 无聊的逗 C++ 详解
题目: 逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中.不过他想到了一个游戏来使他更无聊.他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的 ...
- 蓝桥杯 试题 算法训练 无聊的逗 C++ 详解 - 未完善
题目: 逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中.不过他想到了一个游戏来使他更无聊.他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的 ...
- 蓝桥杯 试题 算法训练 筛选号码 Java
蓝桥杯 试题 算法训练 筛选号码Java 算法训练 筛选号码 资源限制 时间限制:1.0s 内存限制:512.0MB 问题描述 有n个人围成一圈,顺序排号(编号为1到n).从第1个人开始报数(从1到3 ...
- 蓝桥杯 试题 算法训练 无聊的逗
蓝桥杯 试题 算法训练 无聊的逗 问题描述 逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中.不过他想到了一个游戏来使他更无聊.他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘 ...
- 蓝桥杯试题 算法训练 绘制地图
蓝桥杯试题 算法训练 绘制地图 问题描述 最近,WYF正准备参观他的点卡工厂.WYF集团的经理氰垃圾需要帮助WYF设计参"观"路线.现在,氰垃圾知道一下几件事情: 1.WYF的点卡 ...
- 蓝桥杯试题 算法训练 Have You Ever Heard About the Word?
试题 算法训练 Have You Ever Heard About the Word? 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 一个字符串的子串是该字符串的一段连续子序列,如 ...
- 蓝桥杯试题 算法训练 印章
试题 算法训练 印章 C/C++ 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 共有n种图案的印章,每种图案的出现概率相同.小A买了m张印章,求小A集齐n种印章的概率. 输入格式 ...
最新文章
- UVA11137(立方数之和)
- C语言DP备忘计算指数N的斐波那契级数的算法(附完整源码)
- Android之编程中存在性能影响的主要方面
- POJ 1321 棋盘问题(回溯)
- Python《爬取手机和桌面壁纸》
- 了解SQL Server中NOLOCK和WITH NOLOCK表提示的影响
- jquery. Validator验证框架ajax返回json数据
- Struts,Hibernate,Spring经典面试题收藏(转)
- 【ArcGIS】数据属性重新赋值
- 如何修改 Windows10 操作系统里某种文件类型的默认图标
- 如何利用matlab循环读取文件夹中的文件
- Java集合(一)什么是集合
- 实践篇(一):数据准备和本体建模
- 星起航:亚马逊全球开店品牌负责人唐浩表示“中国品牌出海的黄金时代已经到来”
- Android Studio开发(六)短距离无线通信——蓝牙通信
- 芯宇宙通用仓库管理系统V1.0说明书
- 人力资源战略规划新思考
- 5分钟,10行代码!带你用Python做个电脑文件清道夫!
- 做头条自媒体,如何让你的视频作品获得二次流量推荐?
- 还在疑惑并发和并行?
热门文章
- scraino编程流程图_Scraino使用手册V2.0
- 使用Windows自带命令,将文件编码改为ANSI的方法
- 模电(二十一)负反馈放大电路的稳定性及其他问题
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十四)制作主角属性面板及加点器
- 笔记——VNC和FeiQ的安装说明
- 机器人铁锈斑斑好吗_(包天4元)真黑铁丨能排位丨铁锈斑斑机器人丨海盗小炮...
- 2021-2027全球与中国封装基板市场现状及未来发展趋势
- macOS 10.11、macOS 10.12、macOS 10.13、macOS 10.14、macOS 10.15 制作可用于虚拟机安装的 CDR/ISO 系统镜像指导教程
- Java编程思想学习(十二) 数组和容器
- acer p245 linux换win7,小编帮你win7系统在acer安装的修复步骤