首页 > 其他分享 >双向广搜

双向广搜

时间:2024-09-16 20:02:16浏览次数:8  
标签:right goal nums int long 双向 left

双向广搜

  • 用途一:小优化
    • 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.2
    void 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 + 1
        for (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;
    }
};

标签:right,goal,nums,int,long,双向,left
From: https://www.cnblogs.com/sprinining/p/18416554

相关文章

  • uniapp - 最新详细实现web-view网页与安卓苹果App端之间互相通信功能,苹果app/安卓app
    前言在uni-app项目开发中,详解实现web-view和App之间的互相通信完整流程及代码教程,Uniappapp端向webview网站传递数据,同时webview又可以向app端传递数据参数,完成二者的数据通信方案,支持嵌入本地移动端H5页面、第三方网站、自定义网页,附带各种常见问题,解决发送数据通信没......
  • d3.js 构建股权架构图并绘制双向节点树
    效果:代码:StockStructureChart.jsimportReact,{useEffect,useRef}from"react"import*asd3from"d3"constStockStructureChart=({upwardData,downwardData})=>{constref=useRef()constwidth=800constheight=......
  • VUE双向数据绑定
    在Vue.js中,双向数据绑定是其核心特性之一,它允许数据在模型和视图之间自动同步。以下是关于Vue的双向数据绑定的详细说明,包括原理、实现方式和示例。1.双向数据绑定的原理Vue.js通过使用数据劫持和发布-订阅模式实现双向数据绑定。当数据模型发生变化时,视图会自动更新;......
  • Vue双向数据绑定代码解读
     Vue核心基础-CSDN博客数据双向绑定原理_哔哩哔哩_bilibili原理示意图 前置知识reduce()方法用于链式获取对象的属性值Object.defineProperty()方法Object.defineProperty(obj,prop,descriptor)obj:要定义属性的对象。prop:要定义或修改的属性的名称或Symbol......
  • 爆改YOLOv8|利用BiFPN双向特征金字塔改进yolov8
    1,本文介绍BiFPN(BidirectionalFeaturePyramidNetwork)是一种增强特征金字塔网络(FPN)的方法,旨在改善多尺度特征融合。BiFPN的主要创新点包括:双向特征融合:与传统FPN仅在自下而上的方向进行特征融合不同,BiFPN引入了双向融合机制。它不仅从低层特征向高层传递信息,还从高层特征向......
  • Java-实现双向环形链表
            双向链表是一种常用的数据结构,其特点是每个节点不仅包含数据,还持有指向前一个节点和后一个节点的指针。与普通双向链表不同的是,它的哨兵节点的prev指向最后一个元素,而最后一个元素的next指向哨兵。        具体双向普通链表可以参考我的上篇文章,这里......
  • 揭秘 C++ List 容器背后的实现原理,带你构建自己的双向链表
    本篇博客,我们将详细讲解如何从头实现一个功能齐全且强大的C++List容器,并深入到各个细节。这篇博客将包括每一步的代码实现、解释以及扩展功能的探讨,目标是让初学者也能轻松理解。一、简介1.1、背景介绍在C++中,std::list是一个基于双向链表的容器,允许高效的插入和......
  • VMD-CNN-BiLSTM(变分模态分解-卷积神经网络-双向长短记忆网络)组合预测模型
      VMD-CNN-BiLSTM是一种结合了变分模态分解(VariationalModeDecomposition,VMD)、卷积神经网络(ConvolutionalNeuralNetwork,CNN)和双向长短记忆网络(BidirectionalLongShort-TermMemory,BiLSTM)的复合模型。该模型主要用于处理和分析时间序列数据,特别是在预测和分析复......
  • C++学习笔记----6、内存管理(二)---- 数组指针的双向性
            你可能已经看到指针与数组之间的一些重叠。自由内存空间分配的数组由其第一个元素的指针进行访问。栈上的数组通过使用数组语法([])或者正常变量声明来访问。你还会看到的是,其重叠不仅如此,指针与数组有更复杂的关系。1、数组退化至指针        自由内......
  • vue3.2 v-model 双向数据绑定实现
    代码实现示例子组件实现双向绑定<template><divclass="component-name"><inputtype="text":value="modelValue"@input="handleChange"/></div></template><scriptsetuplang=&qu......