蓝桥杯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.hqwc.cn/news/545234.html

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

相关文章

HarmonyOS NEXT应用开发—发布图片评论

介绍 本示例将通过发布图片评论场景&#xff0c;介绍如何使用startAbilityForResult接口拉起相机拍照&#xff0c;并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机&#xff0c;拍照后获取图片地址。 实现思路 创建CommentData类&#…

Rocky Linux 基本工具的安装

1.系统安装后先查看ip地址 ip addr 2.安装net工具 &#xff1a;ifconfig yum install net-tools 3.安装gcc &#xff1b;选择都选 y yum install gcc yum install gcc-c 4.安装tcl yum install -y tcl 5.安装lsof &#xff08;端口查看工具&#xff09; yum install l…

GIF图片压缩的几种方法

影响GIF大小的因素 尺寸大小帧速率颜色动效复杂程度时长 一般输出gif大小是最后一步&#xff0c;所以前期的尺寸&#xff0c;颜色&#xff0c;动效&#xff0c;时长&#xff0c;这些我们都不能动&#xff0c;但是客户往往要求在100kb且不糊的gif,这时候就比较头疼。 方法1&a…

十五、自回归(AutoRegressive)和自编码(AutoEncoding)语言模型

参考自回归语言模型&#xff08;AR&#xff09;和自编码语言模型&#xff08;AE&#xff09; 1 自回归语言模型&#xff08; AR&#xff09; 自回归语言模型&#xff08;AR&#xff09;就是根据上文内容&#xff08;或下文内容&#xff09;预测下一个&#xff08;或前一个&…

【MatLab】之:Simulink安装

一、内容简介 本文介绍如何在 MatLab 中安装 Simulink 仿真工具包。 二、所需原材料 MatLab R2020b&#xff08;教学使用&#xff09; 三、安装步骤 1. 点击菜单中的“附加功能”&#xff0c;进入附加功能管理器&#xff1a; 2. 在左侧的“按类别筛选”下选择Using Simulin…

Docker 安装 Keycloak 开源身份认证服务

Keycloak说明 Keycloak是一款开源的认证授权平台&#xff0c;在Github上已有9.4kStar。Keycloak功能众多&#xff0c;可实现用户注册、社会化登录、单点登录、双重认证 、LDAP集成等功能。 安装 Keycloak 下载 Keycloak 镜像&#xff1a;使用以下命令从 Docker Hub 下载 Keyc…

STM32系列——F103C8T6 控制SG90舵机(HAL库)

文章目录 一、舵机控制原理二、.CubeMX配置配置RCC、SYS、时钟树配置RCC配置SYS配置时钟树配置定时器产生PWM波形 Keil5代码接线图及效果如果您发现文章有错误请与我留言&#xff0c;感谢 一、舵机控制原理 舵机的控制一般需要一个20ms左右的时基脉冲&#xff0c;该脉冲的高电平…

【正则表达式】正则表达式里使用变量

码 const shuai No My Name Is ShuaiGe.match(new RegExp(shuai, gi)); //↑↑↑↑↑↑↑↑ //等同于 //↓↓↓↓↓↓↓↓ /No/gi.test(My Name Is ShuaiGe)用作领域 搜索的字符动态改变&#xff0c;例如↓模糊搜索例&#xff1a; 一个文本宽&#xff0c;输入文本模糊搜索用…

Explain 关键字

优质博文&#xff1a;IT-BLOG-CN explain关键字可以模拟优化器执行 SQL 查询语句&#xff0c;从而知道 MySQL 是如何处理 SQL 语句的。分析查询语句或表结构的性能瓶颈。执行语句&#xff1a;explain SQL语句。表头信息如下&#xff1a; 一、ID 参数 select 查询的序列号&…

免费开源:自动会议记录接口调用|语音识别接口|语音识别API

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

R语言实现中介分析(1)

中介分析&#xff0c;也称为介导分析&#xff0c;是统计学中的一种方法&#xff0c;它用于评估一个或多个中介变量&#xff08;也称为中间变量&#xff09;在自变量和因变量之间关系中所起的作用。换句话说&#xff0c;中介分析用于探索自变量如何通过中介变量影响因变量的机制…

面试经典-33-反转链表 II

题目 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a…