题目:

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

对应 3 代表 若从 开始选,至多选 3 个(满足 <= S) 4 + 1 + 0 <= 10

以此类推……

【分区数组所存放的是:对应原始数组(相同下标),从当前位置往后选,不超过 S 的情况下,最多可以选多少个石子】

得到所有的分区情况,那就开始遍历,找最长相邻区间即可。

1.1 先挑最简单的情况:0开始选,分区结果为2,意味着前S能选2个、选0之后,"去0到的后2位",到5,分区结果为2,意味着(后S)能选2个。综合得:从0开始选,前KK不超过S情况下,可以最多选 2 + 2 = 4个。(满足:前KK不超过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前S1个、选7之后,"去7到的后1位",到4,分区为3,(后S)能选3个。综合得:从7开始选,前KK不超过S情况下,可以最多选 1 + 3 = 4个。(满足:前KK不超过S,但是不满足:K == 后K)

这时候就要改进了。如果选7,为了满足前K不超过S,那么只能选1个,为了前K == 后K,后面也只能选1个,总共2个。那如果不选7,哪怕只在(4 1 0)中选,也最多能选2个。(看着我个人都觉得好绕,但其实并不复杂,应该是我个人表述能力不行……)

所以,当前SK小于后SK,比较(前SK * 2)和(后SK)哪个更大大的即为结果

同理,当前SK大于后SK,比较(前SK)和(后SK * 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++ 详解相关推荐

  1. 蓝桥杯 试题 算法训练 跳马 C++ 详解

     题目: 问题描述 一个8×8的棋盘上有一个马初始位置为(a,b),他想跳到(c,d),问是否可以?如果可以,最少要跳几步? 输入格式 一行四个数字a,b,c,d. 输出格式 如果跳不到,输出-1:否 ...

  2. 蓝桥杯 试题 算法训练 印章 C++ 详解

    题目: 共有n种图案的印章,每种图案的出现概率相同.小A买了m张印章,求小A集齐n种印章的概率. 前言: 建议先看以下两篇文章:(我也是看了之后才懂得) https://blog.csdn.net/o ...

  3. 蓝桥杯 试题 算法训练 无聊的逗 C++ 详解

    题目: 逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中.不过他想到了一个游戏来使他更无聊.他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的 ...

  4. 蓝桥杯 试题 算法训练 无聊的逗 C++ 详解 - 未完善

    题目: 逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中.不过他想到了一个游戏来使他更无聊.他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘成另一个长的,他想知道在两根一样长的 ...

  5. 蓝桥杯 试题 算法训练 筛选号码 Java

    蓝桥杯 试题 算法训练 筛选号码Java 算法训练 筛选号码 资源限制 时间限制:1.0s 内存限制:512.0MB 问题描述 有n个人围成一圈,顺序排号(编号为1到n).从第1个人开始报数(从1到3 ...

  6. 蓝桥杯 试题 算法训练 无聊的逗

    蓝桥杯 试题 算法训练 无聊的逗 问题描述 逗志芃在干了很多事情后终于闲下来了,然后就陷入了深深的无聊中.不过他想到了一个游戏来使他更无聊.他拿出n个木棍,然后选出其中一些粘成一根长的,然后再选一些粘 ...

  7. 蓝桥杯试题 算法训练 绘制地图

    蓝桥杯试题 算法训练 绘制地图 问题描述 最近,WYF正准备参观他的点卡工厂.WYF集团的经理氰垃圾需要帮助WYF设计参"观"路线.现在,氰垃圾知道一下几件事情: 1.WYF的点卡 ...

  8. 蓝桥杯试题 算法训练 Have You Ever Heard About the Word?

    试题 算法训练 Have You Ever Heard About the Word? 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 一个字符串的子串是该字符串的一段连续子序列,如 ...

  9. 蓝桥杯试题 算法训练 印章

    试题 算法训练 印章 C/C++ 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 共有n种图案的印章,每种图案的出现概率相同.小A买了m张印章,求小A集齐n种印章的概率. 输入格式 ...

最新文章

  1. UVA11137(立方数之和)
  2. C语言DP备忘计算指数N的斐波那契级数的算法(附完整源码)
  3. Android之编程中存在性能影响的主要方面
  4. POJ 1321 棋盘问题(回溯)
  5. Python《爬取手机和桌面壁纸》
  6. 了解SQL Server中NOLOCK和WITH NOLOCK表提示的影响
  7. jquery. Validator验证框架ajax返回json数据
  8. Struts,Hibernate,Spring经典面试题收藏(转)
  9. 【ArcGIS】数据属性重新赋值
  10. 如何修改 Windows10 操作系统里某种文件类型的默认图标
  11. 如何利用matlab循环读取文件夹中的文件
  12. Java集合(一)什么是集合
  13. 实践篇(一):数据准备和本体建模
  14. 星起航:亚马逊全球开店品牌负责人唐浩表示“中国品牌出海的黄金时代已经到来”
  15. Android Studio开发(六)短距离无线通信——蓝牙通信
  16. 芯宇宙通用仓库管理系统V1.0说明书
  17. 人力资源战略规划新思考
  18. 5分钟,10行代码!带你用Python做个电脑文件清道夫!
  19. 做头条自媒体,如何让你的视频作品获得二次流量推荐?
  20. 还在疑惑并发和并行?

热门文章

  1. scraino编程流程图_Scraino使用手册V2.0
  2. 使用Windows自带命令,将文件编码改为ANSI的方法
  3. 模电(二十一)负反馈放大电路的稳定性及其他问题
  4. C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十四)制作主角属性面板及加点器
  5. 笔记——VNC和FeiQ的安装说明
  6. 机器人铁锈斑斑好吗_(包天4元)真黑铁丨能排位丨铁锈斑斑机器人丨海盗小炮...
  7. 2021-2027全球与中国封装基板市场现状及未来发展趋势
  8. macOS 10.11、macOS 10.12、macOS 10.13、macOS 10.14、macOS 10.15 制作可用于虚拟机安装的 CDR/ISO 系统镜像指导教程
  9. Java编程思想学习(十二) 数组和容器
  10. acer p245 linux换win7,小编帮你win7系统在acer安装的修复步骤