双向广搜

news/2025/1/15 13:38:31/文章来源:https://www.cnblogs.com/sprinining/p/18416554

双向广搜

  • 用途一:小优化
    • BFS 的剪枝策略,分两侧展开分支,哪侧数量少就从哪侧展开
  • 用途二:
    • 特征:全量样本不允许递归完全展开,但是半量样本可以完全展开。(完全展开的过程中有可能能够进行分组,进行常数级优化
    • 过程:把数据分成两部分,每部分各自展开计算结果,然后设计两部分结果的整合逻辑

127. 单词接龙

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>using namespace std;class Solution {
public:int ladderLength(string beginWord, string endWord, vector<string> &wordList) {// 字符表unordered_set<string> dict;for (const auto &item: wordList)dict.emplace(item);if (dict.find(endWord) == dict.end()) return 0;// 数量小的一侧unordered_set<string> smallLevel;// 数量大的一侧unordered_set<string> bigLevel;// 由数量小的一侧,往外扩展出的下一层unordered_set<string> nextLevel;smallLevel.emplace(beginWord);bigLevel.emplace(endWord);int len = 2;while (!smallLevel.empty()) {// 从小侧扩展,查看这个单词能否通过改动一个字符变成字符表里的某个尚未处理过的字符for (string word: smallLevel) {string nw = word;// 对每个位置尝试换成所有的小写字母for (int i = 0; i < nw.length(); ++i) {char oldCh = nw[i];for (char ch = 'a'; ch <= 'z'; ch++) {// 跳过原本的字符if (ch == oldCh) continue;nw[i] = ch;// 两端碰头if (bigLevel.find(nw) != bigLevel.end()) return len;// 字符表中能找到,也就是说原始字符能变动成字符表中的某个尚未处理过的字符if (dict.find(nw) != dict.end()) {nextLevel.emplace(nw);// 已经处理过dict.erase(nw);}}// 回溯nw[i] = oldCh;}}if (nextLevel.size() > bigLevel.size()) {// 由 smallLevel 扩展出的 nextLevel 集合元素比 bigLevel 多auto newSmall = bigLevel;auto newBig = nextLevel;smallLevel = newSmall;bigLevel = newBig;} else {auto newSmall = nextLevel;smallLevel = newSmall;}nextLevel.clear();len++;}return 0;}
};

P4799 [CEOI2015 Day2] 世界冰球锦标赛

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_set>
#include <algorithm>using namespace std;long N, M;
vector<long> prices;// sums 中记录 prices 中从 left 到 right 为止,每个位置选或者不选所得到的结果中小于等于 M 的
void generate(int left, int right, long tempSum, vector<long> &sums) {if (tempSum > M) return;if (left > right) {sums.emplace_back(tempSum);return;};// 不要 prices[left]generate(left + 1, right, tempSum, sums);// 要 prices[left]generate(left + 1, right, tempSum + prices[left], sums);
}int main() {cin >> N >> M;prices.resize(N);for (int i = 0; i < N; ++i)cin >> prices[i];// 把数据分成两部分,每部分各自展开计算结果vector<long> leftSum;vector<long> rightSum;long mid = N >> 1;generate(0, mid - 1, 0, leftSum);generate(mid, N - 1, 0, rightSum);// 排序,方便整合两个部分的计算结果sort(leftSum.begin(), leftSum.end());sort(rightSum.begin(), rightSum.end());// 总的购买方案long res = 0;long lSize = leftSum.size();long rSize = rightSum.size();long left = 0;long right = rSize - 1;// 每次累加的是在以先选择左侧结果中总费用的情况下,从右侧结果中可选择的方案数while (left < lSize) {while (right >= 0 && leftSum[left] + rightSum[right] > M)right--;res += right + 1;left++;}cout << res;
}

1755. 最接近目标值的子序列和

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;class Solution {
public:void generate(vector<int> &nums, int left, int right, long sum, vector<long> &sums) {if (left == right + 1) {sums.emplace_back(sum);return;}// 子序列中不包含 nums[left]generate(nums, left + 1, right, sum, sums);// 子序列中包含 nums[left]generate(nums, left + 1, right, sum + nums[left], sums);}int minAbsDifference(vector<int> &nums, int goal) {vector<long> leftSums;vector<long> rightSums;int mid = nums.size() >> 1;generate(nums, 0, mid - 1, 0, leftSums);generate(nums, mid, nums.size() - 1, 0, rightSums);sort(begin(leftSums), end(leftSums));sort(begin(rightSums), end(rightSums));long lSize = leftSums.size();long rSize = rightSums.size();long left = 0;long right = rSize - 1;long res = LONG_MAX;// 考察每种在 leftSums[left] 的方案下,rightSums 中最满足要求的方案 rightSums[right]// 这样就不会遗漏由于分成两个部分进行全排列所丢失的那部分方案// 优化就是在总和小于目标时,应该让方案得到的序列和变大,也就是 left++// 而不是继续固定死 left,去 right--,因为 right-- 只会让序列和更小while (left < lSize && right >= 0) {int t = leftSums[left] + rightSums[right];if (t == goal) {return 0;} else if (t < goal) {if (goal - t < res) res = goal - t;left++;} else if (t > goal) {if (t - goal < res) res = t - goal;right--;}}return res;}
};
  • 常数级优化
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;class Solution {
public:// 常数级优化 2.2void generate(vector<int> &nums, int left, int right, long sum, vector<long> &sums) {if (left == right + 1) {sums.emplace_back(sum);return;}// left 作为这组相同数字的开头// 把 cur 移动到这组相同数字的末尾int cur = left;while (cur + 1 <= right && nums[cur + 1] == nums[cur])cur++;// 这组数字的长度int len = cur - left + 1;// 剪枝:这组数字选 i 个,选哪几个无所谓。从 2 ^ len 减少到 len + 1for (int i = 0; i <= len; ++i)generate(nums, cur + 1, right, sum + i * nums[left], sums);}int minAbsDifference(vector<int> &nums, int goal) {// 常数级优化 1// 负数累加和long negativeSum = 0;// 正数累加和long positiveSum = 0;for (int i = 0; i < nums.size(); ++i) {if (nums[i] > 0) positiveSum += nums[i];if (nums[i] < 0) negativeSum += nums[i];}if (negativeSum > goal) return negativeSum - goal;if (positiveSum < goal) return goal - positiveSum;// 常数级优化 2.1// 排序主要是为了将相同的数字靠在一起,在暴力递归的时候分组递归,而不是按照每个位置进行递归sort(nums.begin(), nums.end());vector<long> leftSums;vector<long> rightSums;int mid = nums.size() >> 1;generate(nums, 0, mid - 1, 0, leftSums);generate(nums, mid, nums.size() - 1, 0, rightSums);sort(begin(leftSums), end(leftSums));sort(begin(rightSums), end(rightSums));long lSize = leftSums.size();long rSize = rightSums.size();long left = 0;long right = rSize - 1;long res = LONG_MAX;// 考察每种在 leftSums[left] 的方案下,rightSums 中最满足要求的方案 rightSums[right]// 这样就不会遗漏由于分成两个部分进行全排列所丢失的那部分方案// 优化就是在总和小于目标时,应该让方案得到的序列和变大,也就是 left++// 而不是继续固定死 left,去 right--,因为 right-- 只会让序列和更小while (left < lSize && right >= 0) {int t = leftSums[left] + rightSums[right];if (t == goal) {return 0;} else if (t < goal) {if (goal - t < res) res = goal - t;left++;} else if (t > goal) {if (t - goal < res) res = t - goal;right--;}}return res;}
};

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

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

相关文章

三、redis之strings类型

strings是redis中使用最多的类型。 redis官网中是这么描述strings的: Redis strings store sequences of bytes, including text, serialized objects, and binary arrays. 可以看到Redis strings保存的是sequences of bytes,也就是字节序列。不仅可以保存字符串,而且还可以…

排队论——数学模型和绩效指标精解

排队论最早由丹麦工程师Agner Krarup Erlang于1910年提出,旨在解决自动电话系统的问题,成为话务理论的奠基石。Erlang通过研究电话呼叫的随机到达和服务时间,推导出著名的埃尔朗电话损失率公式,用于计算电话系统的呼叫阻塞率,揭示了排队现象的本质。Erlang之后,排队论得到…

本地文件包含漏洞详解与CTF实战

1. 本地文件包含简介 1.1 本地文件包含定义 本地文件包含是一种Web应用程序漏洞,攻击者通过操控文件路径参数,使得服务器端包含了非预期的文件,从而可能导致敏感信息泄露。 常见的攻击方式包括:包含配置文件、日志文件等敏感信息文件,导致信息泄露。 包含某些可执行文件或…

代码随想录算法 - 二叉树6

题目1235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖…

6、函数的声明

程序是一行行执行,我们可以在前面进行函数声明,然后将函数的定义放在程序末尾。 声明可以写多次,但是定义只能写一次。

如何基于Java解析国密数字证书

一、说明 随着信息安全的重要性日益凸显,数字证书在各种安全通信场景中扮演着至关重要的角色。国密算法,一、说明 随着信息安全的重要性日益凸显,数字证书在各种安全通信场景中扮演着至关重要的角色。国密算法,作为我国自主研发的加密算法标准,其应用也愈发广泛。然而,在…

Windows应急响应-个人整理

个人总览-仍待完善Windows应急响应整理(一)参考 1.NOPTeam的手册链接 2.fox-yu的博客(思路很清晰,对我这个小白来说很友好)1.整体思路 1.1常见事件类型(不完整、待补充)网络协议攻击:拒绝服务攻击:DDos、CC攻击、泛洪攻击等。链接 DNS劫持 ARP欺骗web入侵:webshell 网页挂马…

视野修炼-技术周刊第101期 | 垂直居中

① align-content - 垂直居中普通元素 ② up mode - 一键隐藏 Chrome 插件 ③ Chrome 性能面板新功能 ④ k-colors.js - 图片主色提取 ⑤ 英:优化JS性能的一些技巧 ⑥ 英:Web 的剪贴板,如何存储不同类型的数据 ⑦ 英:不简单的 js 入门教程 ⑧ Peter Cat - GitHub 仓库智能答…

06: 抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类1. 案例 在Access和SQL server分别插入User表和Department表 2. 抽象工厂模式结构 - 抽象产品(AbstractProduct):所有产品的基类,提供产品类的公共方法struct User {std::string m_sName = "";…

南沙C++信奥老师解一本通题 1371:看病

​【题目描述】有个朋友在医院工作,想请BSNY帮忙做个登记系统。具体是这样的,最近来医院看病的人越来越多了,因此很多人要排队,只有当空闲时放一批病人看病。但医院的排队不同其他排队,因为多数情况下,需要病情严重的人优先看病,所以希望BSNY设计系统时,以病情的严重情…

堪称最优秀的 Docker 可视化管理工具 ——Portainer

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理 Docker 环境,包括单机环境和集群环境。随着 Docker 内实例越来越多,就得涉及到监控以及统计的需求:有多少个容器?运行的有几个?有哪些容器 CPU 使用率低?... Portainer 是一款轻量级的应用,它提供了图…

分布式数据库中间件:MyCat 和 ShardingSphere

分布式数据库中间件 用于实现 分库、分表、分片、分布式事务、读写分离 等。 本文 是 调查 MyCat 和 ShardingSphere 两款 中间件 的一些信息汇总。本文时间:2024年9月。MyCat Mycat数据库分库分表中间件。ben发布于博客园 http://www.mycat.org.cn github-Mycat1 https://gi…