蓝桥杯2023年省A(一波三折的)【买瓜】折半搜索+剪枝+排序

题目:洛谷 P9234 [蓝桥杯 2023 省 A] 买瓜


折半搜索

一开始觉得像dp,试着写了,显然过不了,但我实在觉得搜索也过不了啊,去看题解,发现使用了折半搜索(每天都觉得啥都不会捏
折半搜索就是先搜一半,记录下答案,再搜一半,最后把答案整合在一起
比如这题,每个数有三种选法,复杂度3n,折半后就是2*3n/2,大大降低了
但是这个复杂度还是过不了,后面就是不停优化剪枝

剪枝
剪枝1

当前和已经超过m (sum > m)

剪枝2

当前劈瓜次数已经超过历史最优 (k >= ans)

剪枝3

达到同样的和,曾经有过劈瓜数更小的方案 (mp.count(sum) && mp[sum] < k)

折半+剪枝(76分)
用unordered_map更快

#include <vector>
#include <iostream>
#include <cstdio>
#include <map> 
#include <unordered_map>
#include <ctime> 
using namespace std;typedef long long ll;
const int N = 35;ll a[N];
int ans = N;
unordered_map<ll, int> mp;//unordered_map更快
ll n, m;void dfs1(int i, int k, ll sum)
{if (sum > m) return;//超了,剪if (k >= ans) return;if (sum == m)//到了,走了{ans = min(ans, k);return; }if (mp.count(sum) && mp[sum] < k) return;//有过更优情况,剪!if (i > n / 2)//到头了,把搜到的东西记一下{//用map记录达到sum的砍刀数最小值if (mp.count(sum)) mp[sum] = min(mp[sum], k);else mp[sum] = k;return;}//先搜贡献大的更容易找到答案(大概吧dfs1(i + 1, k, sum + a[i] + a[i]);//整个dfs1(i + 1, k + 1, sum + a[i]);//半个dfs1(i + 1, k, sum); //不要
}void dfs2(int i, int k, ll sum)
{if (sum > m) return;if (k >= ans) return;if (sum == m){ans = min(ans, k);return; }if (i > n){//如果另一半有匹配的就更新答案if (mp.count(m - sum)) ans = min(ans, k + mp[m - sum]);return;}dfs2(i + 1, k, sum + a[i] + a[i]);dfs2(i + 1, k + 1, sum + a[i]);dfs2(i + 1, k, sum); 
}
int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);cin >> n >> m;m += m;//为了不出现小数,把a[i]当半个瓜用,那么m也要翻倍for (int i = 1; i <= n; i++){cin >> a[i];}dfs1(1, 0, 0);dfs2(n/2+1, 0, 0);if (ans == N) cout << -1;else cout << ans;return 0;
}

继续看题解,发现很重要的一点是要给数组排序
为什么呢,不知道,大家都不说
好像挺有道理的但是我说不出道理,先放着

加上sort之后A了


觉得自己无敌了吧?来看看这个
题目 3145: 蓝桥杯2023年第十四届省赛真题-买瓜
欸~一模一样的代码只有82分
最后是怎么过的呢,要把数组从大到小排序
为什么呢
我的理解是,在搜第二段的时候,前面已经出现很多凑到m的情况,使ans变小了,所以第二段会被剪得更狠;相比之下第一段则几乎都要搜到头,所以重点在于要把第一段剪了。那有没有办法让第一段多剪掉一点呢,就让第一段都是大数,sum容易超过m,就会多多被剪了

加上从大到小排序:

sort(a + 1, a + n + 1, greater<int>()); 

正当我欢天喜地地准备结束这题的时候,我手欠地把从大到小版交到了洛谷
嘿——您猜怎么着?TLE了!
好吧我前边都是一顿瞎说(但我真是觉得有道理啊)
那只能是数据问题了,但是数据问题要怎么A啊!

剪枝4

后面我又翻看题解,发现了一个新的剪枝:
预处理后缀和,当前sum加上后缀和也够不到m的话就直接剪
注意这个剪枝只能在第一段中使用,因为第二段本身就要匹配第一段的答案,没法判断会不会不够

void dfs1(int i, int k, ll sum)
{if (sum > m) return;if (k >= ans) return;if (sum + suf[i] < m) return;//注意第i个还没加过,所以是判断sum + suf[i]if (sum == m){ans = min(ans, k);return; }if (i > n / 2){if (mp.count(sum)) mp[sum] = min(mp[sum], k);else mp[sum] = k;return;}dfs1(i + 1, k, sum + a[i] + a[i]);dfs1(i + 1, k + 1, sum + a[i]);dfs1(i + 1, k, sum); 
}

还有个注意点,要sort之后再算后缀和(对就是我这么傻
以及因为我存的a[i]算半个瓜,算后缀和要算整个瓜,所以要加两次

sort(a + 1, a + n + 1, greater<int>()); suf[n + 1] = 0;for (int i = n; i >= 1; i--){suf[i] = suf[i + 1] + a[i] + a[i];}

贴一个两边都能A的代码

#include <vector>
#include <iostream>
#include <cstdio>
#include <map> 
#include <unordered_map>
#include <ctime> 
#include <algorithm>
using namespace std;typedef long long ll;
const int N = 35;ll a[N], suf[N];
int ans = N;
unordered_map<ll, int> mp;
ll n, m;void dfs1(int i, int k, ll sum)
{if (sum > m) return;//超了,剪if (k >= ans) return;if (sum + suf[i] < m) return;//注意第i个还没加过,所以是判断sum + suf[i]if (sum == m)//到了,走了{ans = min(ans, k);return; }if (mp.count(sum) && mp[sum] < k) return;//有过更优情况,剪!if (i > n / 2)//到头了,把搜到的东西记一下{//用map记录达到sum的劈瓜数最小值if (mp.count(sum)) mp[sum] = min(mp[sum], k);else mp[sum] = k;return;}//先搜贡献大的更容易找到答案(大概吧dfs1(i + 1, k, sum + a[i] + a[i]);//整个dfs1(i + 1, k + 1, sum + a[i]);//半个dfs1(i + 1, k, sum); //不要
}void dfs2(int i, int k, ll sum)
{if (sum > m) return;if (k >= ans) return;if (sum == m){ans = min(ans, k);return; }if (i > n){//如果另一半有匹配的就更新答案if (mp.count(m - sum)) ans = min(ans, k + mp[m - sum]);return;}dfs2(i + 1, k, sum + a[i] + a[i]);dfs2(i + 1, k + 1, sum + a[i]);dfs2(i + 1, k, sum); 
}
int main()
{ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);cin >> n >> m;m += m;//为了不出现小数,把a[i]当半个瓜用,那么m也要翻倍for (int i = 1; i <= n; i++){cin >> a[i];}//sort(a + 1, a + n + 1); sort(a + 1, a + n + 1, greater<int>()); suf[n + 1] = 0;for (int i = n; i >= 1; i--){suf[i] = suf[i + 1] + a[i] + a[i];}dfs1(1, 0, 0);dfs2(n/2+1, 0, 0);if (ans == N) cout << -1;else cout << ans;return 0;
}

结果就是加上这个剪枝从大到小排序洛谷能过了,但是从小到大c语言网不行,继续看!


其他优化

我就不写了感觉好麻烦要把之前写的推翻重来orz

二分

不直接搜i+1,而是根据还需要的斤数在剩下的瓜里二分(因为已经排序了嘛),大于还需要的斤数的瓜可以不用考虑
(这样二分甚至可以不用折半搜索
见 P9234 [蓝桥杯 2023 省 A] 买瓜 题解

手写哈希表

大概是因为unordered_map还是常数大了吧(
见 买瓜 题解


参考

P9234 [蓝桥杯 2023 省 A] 买瓜 题解
买瓜 题解
买瓜题解


菜死我算了,真在赛场上碰到这种题我就拿个30分吧(默哀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/750337.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wayland(xdg_wm_base) + egl + opengles 渲染使用纹理贴图的旋转 3D 立方体实例(十三)

文章目录 前言一、使用 stb_image 库加载纹理图片1. 获取 stb_image.h 头文件2. 使用 stb_image.h 中的相关接口加载纹理图片3. 纹理图片——cordeBouee4.jpg二、渲染使用纹理贴图的旋转 3D 立方体1. egl_wayland_texture_cube.c2. Matrix.h 和 Matrix.c3. xdg-shell-client-pr…

日期与时间(Java)

文章目录 日期与时间&#xff08;Java&#xff09;一、JDK8之前的1.1 Date1.2 SimpleDateFormat1.3 Calendar 二、 JDK8之后的2.1 LocalDate、LocalTime和LocalDateTime2.2 ZoneId和ZonedDateTime2.3 Instant2.4 DateTimeFormatter2.4 Period和 Duration &#x1f389;写在最后…

Java项目利用Redisson实现真正生产可用高并发秒杀功能 支持分布式高并发秒杀

Java中的高并发秒杀场景下我们可以使用redisson来实现高并发秒杀功能, 以下就是一个可用于生产环境的高并发秒杀示例代码: pom依赖 <!-- https://mavenlibs.com/maven/dependency/org.redisson/redisson --><dependency><groupId>org.redisson</groupId&…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Web)下篇

onRequestSelected onRequestSelected(callback: () > void) 当Web组件获得焦点时触发该回调。 示例&#xff1a; // xxx.ets import web_webview from ohos.web.webviewEntry Component struct WebComponent {controller: web_webview.WebviewController new web_webv…

Github Copilot 工具,无需账号,一键激活

① 无需账号&#xff0c;100%认证成功&#xff01;0风险&#xff0c;可联网可更新&#xff0c;&#xff0c;支持copilot版本升级&#xff0c;支持chat ② 支持windows、mac、linux系统等设备 ③一号通用&#xff0c;支持所有IDE(AppCode,CLion,DataGrip,GoLand,IntelliJ IDEA …

线程的 run()和 start()有什么区别?调用start()方法时,线程状态的变化是怎么样的?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 线程的 run()和 start()有什么区别 run()方法 run()方法是Thread类中的一个普通方法,用来定义线程的执行逻辑。当一个类继承自Thread类并且重写了run()方法…

LeetCode题练习与总结:解数独

一、题目 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; …

Linux 基础-查看和设置环境变量

一&#xff0c;查看环境变量 在 Linux中&#xff0c;环境变量是一个很重要的概念。环境变量可以由系统、用户、Shell 以及其他程序来设定&#xff0c;其是保存在变量 PATH 中。环境变量是一个可以被赋值的字符串&#xff0c;赋值范围包括数字、文本、文件名、设备以及其他类型…

前端跨平台开发框架:简化多端开发的利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

OpenCV系列文章目录(持续更新中......)

引言&#xff1a; OpenCV是一个开源的计算机视觉库&#xff0c;由英特尔公司开发并开源的一组跨平台的C函数和少量的C函数组成&#xff0c;用于实时图像处理、计算机视觉和机器学习等应用领域。OpenCV可以在包括Windows、Linux、macOS等各种操作系统平台上使用&#xff0c;具…

数据结构--七大排序算法(更新ing)

下面算法编写的均是按照由小到大排序版本 选择排序 思想&#xff1a; 每次遍历待排序元素的最大下标&#xff0c;与待排序元素中最后一个元素交换位置&#xff08;此时需要设置一个临时变量来存放下标&#xff09; 时间复杂度--O(n^2) 空间复杂度--O(1) 稳定性--不稳定 代码实…

解析服务器下载速度:上行、下行与带宽之谜

在日常使用中&#xff0c;我们经常会遇到从服务器下载内容速度忽快忽慢的情况&#xff0c;即便服务器的硬件配置如4核CPU、8GB内存和12Mbps的带宽看似足够。为何会出现这种现象&#xff1f;这背后涉及到网络中的上行、下行以及带宽等关键概念。本文旨在揭开这些术语背后的含义&…

用Origin快速拟合荧光寿命、PL Decay (TRPL)数据分析处理

需要准备材料&#xff1a;Origin、PL Decay数据txt文件 首先打开Origin画图软件 导入数据&#xff0c;按照下图箭头操作直接导入 双击你要导入的PL Decay的txt数据文件&#xff0c;然后点OK 继续点OK 数据导入后首先删除最大光子数之前的无效数据&#xff0c;分析的时候用…

计算机设计大赛 题目:基于机器视觉opencv的手势检测 手势识别 算法 - 深度学习 卷积神经网络 opencv python

文章目录 1 简介2 传统机器视觉的手势检测2.1 轮廓检测法2.2 算法结果2.3 整体代码实现2.3.1 算法流程 3 深度学习方法做手势识别3.1 经典的卷积神经网络3.2 YOLO系列3.3 SSD3.4 实现步骤3.4.1 数据集3.4.2 图像预处理3.4.3 构建卷积神经网络结构3.4.4 实验训练过程及结果 3.5 …

html元素基本使用

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;第一次学习前端的html&#xff0c;写一篇笔记总结常用的元素 语义化 例如只要是 不管字体的大小是怎么样&#xff0c;有没有加粗都是标题&#xff0c;元素显示到页面中的效果应该由css决定&#xff0c;这就是语义化。 文…

特殊内齿轮加工的另一种选择

内齿轮加工普遍采用插齿或拉削&#xff0c;但对于一些特殊齿廓的内齿轮来说&#xff0c;插齿可能会有一定的困难&#xff0c;或者成本较高。在这种情况下&#xff0c;线切割加工不失为一种不错的选择。那么什么样的零件需要选择这种加工方式呢&#xff1f;一起来看看&#xff1…

【其他】sd卡的照片在相机上能看到在电脑上却看不到

sd卡的照片在相机上能看到在电脑上却看不到 前情提要&#xff1a;太长不看版解决办法&#xff1a;思路&#xff1a;一、首先考虑恢复数据二、 解决文件后缀是exe的问题 前情提要&#xff1a; 在相机里可以看到照片和视频&#xff0c;但是SD卡通过读卡器插入电脑看不到&#x…

Mr-Robot1靶场练习靶场推荐小白入门练习靶场渗透靶场bp爆破wordpress

下载链接&#xff1a; Mr-Robot: 1 ~ VulnHub 安装&#xff1a; 打开vxbox&#xff0c;菜单栏----管理----导入虚拟电脑 选择下载完的ova文件&#xff0c;并修改想要保存的位置&#xff08;也可以保持默认位置&#xff09; 导入完成后可以根据自己的情况去配置网络链接方式 完成…

数学建模--MATLAB基本使用

1.线性方程组 这个是一个线性方程组&#xff08;属于线性代数的范畴&#xff09;&#xff0c;Axb类型的方程&#xff0c;如果使用MATLAB进行求解&#xff0c;就需要分别表示A矩阵&#xff08;线性方程组未知数前面的系数&#xff09;&#xff0c;b矩阵&#xff08;表示等式右边…

Linux应用编程 - sysfs方式操作GPIO

简介&#xff1a; 应用层想要对底层硬件进行操控&#xff0c;通常可以通过两种方式 /dev/目录下的设备文件&#xff08;设备节点&#xff09;​​​​​​​/sys/目录下设备的属性文件 通常情况下&#xff0c;简单地设备会使用 sysfs 方式操控&#x…