每周一算法:双向深搜

news/2024/7/20 22:24:40 标签: 算法, 青少年编程, 信息学竞赛, c++, 深度优先

题目描述

达达帮翰翰给女生送礼物,翰翰一共准备了 N N N 个礼物,其中第 i i i 个礼物的重量是 G [ i ] G[i] G[i]

达达的力气很大,他一次可以搬动重量之和不超过 W W W的任意多个物品。

达达希望一次搬掉尽量重的一些物品,请你告诉达达在他的力气范围内一次性能搬动的最大重量是多少。

输入格式

第一行两个整数,分别代表 W W W N N N
以后 N N N行,每行一个正整数表示 G [ i ] G[i] G[i]

输出格式

仅一个整数,表示达达在他的力气范围内一次性能搬动的最大重量。

数据范围

1 ≤ N ≤ 46 1≤N≤46 1N46,
1 ≤ W , G [ i ] ≤ 2 31 − 1 1≤W,G[i]≤2^{31}−1 1W,G[i]2311

输入样例

20 5
7
5
4
18
1

输出样例

19

算法思想

根据题目描述,需要从给定的 N N N个数中选择几个,使它们的和最接近 W W W,属于“子集和”问题的扩展;当然也可以从背包问题的角度去思考解决,但是这里背包的“体积”过大( 1 ≤ W ≤ 2 31 − 1 1≤W≤2^{31}−1 1W2311),时间和空间复杂度都不允许。

这类问题的直接解法就是进行“指数型”枚举,搜索每个礼物选还是不选,时间复杂度为 O ( 2 N ) O(2^N) O(2N)。此题 N ≤ 46 N≤46 N46 2 46 2^{46} 246的复杂度过高,可以利用双向深搜的思想进行优化。

双向深搜

除了迭代加深之外,双向深搜也可以避免在深层子树上浪费时间。

在一些题目中,问题不但具有“初态”,还具有明确的“终态”,并且从初态开始搜索与从终态开始逆向搜索产生的搜索树都能覆盖整个状态空间。在这种情况下,就可以采用双向搜索——从初态和状态出发各搜索一半,产生的两棵深度减半的搜索树,在中间交汇、组成最终答案。避免了层数过深时,分支数量的大规模增长。

下图是直接进行一次搜索产生的搜索树:
在这里插入图片描述
下图是双向搜索的两棵搜索树,避免了避免了层数过深时,分支数量的大规模增长。
在这里插入图片描述

算法实现

将礼物分成两部分

  • 首先,从前一半礼物中枚举出所有组合,将可能达到 0 ∼ W 0\sim W 0W之间的所有重量值,存放在一个数组 a [ ] a[] a[]中,并对数组进行排序、去重
  • 然后,进行第二次搜索,尝试从后一半礼物中枚举出所有组合,对于每个可能达到的重量 k k k,在第一部分得到的数组 a [ ] a[] a[]中查找 k + a [ i ] ≤ W k+a[i]\le W k+a[i]W的最大值,可以使用二分查找。

这样,算法的时间复杂度为 O ( 2 N 2 × l o g N 2 ) O(2^{\frac{N}{2}}\times log\frac{N}{2}) O(22N×log2N)

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 50;
int w[N];
int cnt, a[1 << 25]; //存储前一半礼物所有组合的重量
int n, W, ans;
void dfs1(int i, int k) //k表示目前组合的重量
{
    if(i == n / 2) //只搜索前一半的礼品
    {
        a[cnt ++] = k; //将组合得到的重量存到a数组
        return;
    }
    if((LL)k + w[i] <= W) dfs1(i + 1, k + w[i]); //装得下第i件礼物
    dfs1(i + 1, k); //不装第i件礼物
}
void dfs2(int i, int k)
{
    if(i == n) //搜索完成,二分查找不超过W的最大组合重量
    {
        int L = 0, R = cnt - 1;
        while(L < R)
        {
            int mid = (L + R + 1) / 2;
            if((LL)k + a[mid] <= W) L = mid;
            else R = mid - 1;
        }
        ans = max(ans, k + a[L]);
        return;
    }
    if((LL)k + w[i] <= W) dfs2(i + 1, k + w[i]); //装得下第i件礼物
    dfs2(i + 1, k); //不装第i件礼物
}
int main()
{
    cin >> W >> n;
    for(int i = 0; i < n; i ++) cin >> w[i];
    //优化搜索顺序,优先搜索重量较大的礼品
    sort(w, w + n);
    reverse(w, w + n);
    dfs1(0, 0); //枚举前一半礼品的组合,将其组合得到的重量存到a数组
    sort(a, a + cnt); //对前一半礼物组合得到的重量排序去重
    cnt = unique(a, a + cnt) - a;
    //对后一半礼物进行搜索
    dfs2(n / 2, 0);
    cout << ans;
    return 0;
}

http://www.niftyadmin.cn/n/5434731.html

相关文章

华为配置Hotspot2.0无线网络示例

配置Hotspot2.0无线网络示例 组网图形 图1 配置Hotspot2.0无线网络组网图 组网需求配置思路配置注意事项操作步骤配置文件 组网需求 某网络服务商在原有移动网络业务的基础上&#xff0c;新增部署WLAN网络接入业务&#xff0c;为用户提供更好的网络体验。但传统的WLAN网络业…

GPU密集型计算性能优化的方法和技术

对GPU密集型计算进行性能优化的方法和技术多种多样。通过一些优化策略和技术需要综合考虑应用程序的具体需求、所使用的GPU硬件、以及编程模型和库的选择。通过不断地分析和调整&#xff0c;可以实现GPU计算性能的持续提升。以下是一些常用的优化策略和技术&#xff1a; 算法优…

zookeeper快速入门四:在java客户端中操作zookeeper

系列文章&#xff1a; zookeeper快速入门一&#xff1a;zookeeper安装与启动-CSDN博客 zookeeper快速入门二&#xff1a;zookeeper基本概念-CSDN博客 zookeeper快速入门三&#xff1a;zookeeper的基本操作 先启动zookeeper服务端。 在maven引入zookeeper依赖。 <depende…

Jenkins通知目标服务器拉取Harbor镜像部署

1.告诉目标服务器拉取哪个镜像 2.判断当前有没有正在运行此容器&#xff0c;有就删除 3.接着查看拉取的镜像目标服务器上是否已存在&#xff0c;有就删除 4.拉取Harbor镜像 5.运行容器 目标服务器编写脚本 创建个部署脚本 vim deploy.sh告诉目标服务器Harbor地址、仓库、镜像…

ChatGLM3-6B独立部署提供HTTP服务failed to open nvrtc-builtins64_121.dll

背景 我在本地windoes部署ChatGLM3-bB&#xff0c;且希望部署后能提供HTTP server的能力。 模型部署且启动是成功了&#xff0c;但是在访问生成接口/v1/chat/completions时报错failed to open nvrtc-builtins64_121.dll。 问题详细描述 找不到nvrtc-builtins64_121.dll Runtime…

智能合约设计模式:讲解代理模式及其安全漏洞

苏泽 大家好 这里是苏泽 一个钟爱区块链技术的后端开发者 本篇专栏 ←持续记录本人自学两年走过无数弯路的智能合约学习笔记和经验总结 如果喜欢拜托三连支持~ 我们首先来看看什么是设计模式 和我们软件工程里面的设计模式有什么异同&#xff1f; 智能合约设计模式是一种在区…

【人工智能】英文学习材料01(每日一句)

&#x1f33b;个人主页&#xff1a;相洋同学 &#x1f947;学习在于行动、总结和坚持&#xff0c;共勉&#xff01; 目录 1.Natural Language Processing&#xff0c;NLP&#xff08;自然语言处理&#xff09; 2.Machine Learing&#xff0c;ML&#xff08;机器学习&#xf…

Hive调优总结

参数设置 概述:hive的参数配置, 就是在那里配置hive的参数信息, 根据配置地方不同, 作用范围也不一样. 配置方式: 1. set方式进行设置. 2. 命令行方式进行设置. 3. 配置文件方式进行设置. 优先级问题: set方式 > 命令行方式 > 配置文件…