深度优先搜索案例思路解析

简介

本文基于几个示例题,探讨了两种常用的深度优先搜索思路。旨在总结所做题目的经验,并帮助读者扩展思路,更好地理解深度优先搜索算法。

1.题目介绍

1.1 LeeCode [39] 组合总和

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示 :

1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40
1.11 解题思路一

        在进行深度搜索时,我们需要从目标数组中选择一个值作为当前路径的元素。这个值是通过对目标数组进行遍历获得的,并将其记录在路径数组中,也称为"路径"。由于题目中允许数据重复选择,所以下一层的递归仍然是从当前选择元素的下标开始遍历目标数组,然后继续选择下一个路径元素。如果路径中所有值的总和恰好等于目标整数,则将该路径记录在二维数组中。 在路径中,如果继续选择目标数组的其他值,则没有意义,因此应中断该路径的延伸。另外,如果路径中的和超过目标值,则继续选择其他值也同样无意义,因此同样需要中断该路径的延伸。

        需要特别注意的是,在记录路径中的所有数值时,当对路径数组进行遍历并在每次递归中进行值的选择时,务必确保在选择新值之前清除之前路径中选择的值,然后再进行新的值的选择。

1.12 代码实现一
class Solution {
    vector<vector<int>>paths;
    vector<int>path;
    int i,sum;
public:
    vector<vector<int>> combinationSum(vector<int>& cand, int target) {
        DFS(cand,target,0);
        return paths;
    }
    void DFS(vector<int>arr,int target,int m){
        if(sum==target){  //如果当前路径的和已经等于要找的值,就没必须再去搜索
            paths.push_back(path);
            return;
        }
        if(sum>target) return; //如果当前路径的和大于要找的值,此时这条路径错误
            
        for(int i=m;i<arr.size();i++){  //每次递归都从数组里面选取一个值
            path.push_back(arr[i]);
            sum+=path.back();
            DFS(arr,target,i);
            sum-=path.back();
            path.pop_back();
    }
  }
};
1.13 解题思路二

        在进行搜索时,通常按照遍历数组的顺序进行。对于数组的每个元素,我们考虑两种情况:一是不选择当前数组下标中的元素,二是选择当前数组下标的元素。然后,我们递归地继续搜索,并寻找符合条件的路径数组。

1.14 代码实现二
class Solution {
   vector<vector<int>> paths;
    vector<int> path;
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        DFS(candidates, target, 0, 0);
        return paths;
    }
    
    void DFS(vector<int>& candidates, int target, int sum, int start) {
        if (sum > target||start>=candidates.size()) { //当前路径数组的和大于目标值或者数组下标越界则结束
            return;
        }
        if (sum == target) {  //当前路径和等于目标值,当前路径已经满足要求,再去选取别的值一定不正确,提交结束当前路径
            paths.push_back(path);
            return;
        }
            //每次递归都考虑数组下标为i的值选不选
            DFS(candidates, target, sum, start + 1);
            path.push_back(candidates[start]);
            DFS(candidates, target, sum + candidates[start], start);
            path.pop_back();
    }
};

1.2 蓝桥杯2023年第十四届省赛真题-买瓜

1.21 题目描述

        小蓝正在一个瓜摊上买瓜。瓜摊上共有 n 个瓜,每个瓜的重量为 Ai 。 小蓝刀功了得,他可以把任何瓜劈成完全等重的两份,不过每个瓜只能劈一刀。 小蓝希望买到的瓜的重量的和恰好为 m 。 请问小蓝至少要劈多少个瓜才能买到重量恰好为 m 的瓜。如果无论怎样小蓝都无法得到总重恰好为 m 的瓜,请输出 −1 。

1.22 解题思路

参考上述组合总和题的解题思路二,仅需注意其边界条件依旧递归中的变量参数即可

        特别需要注意的是,这是一个最小值求解的问题。因此,在开始时,将要求的最小值变量设置为一个较大的值是至关重要的。因为深度搜索的第一次找到的路径并不一定是最小值,所以我们需要在不同的递归路径中持续更新这个最小值,以此找到最优解。

1.23 代码实现
#include<iostream>
using namespace std;
int n = 0, m = 0, nums[30], Min = 100;
long arr[31];

int dfs(int i, double sum, int c) {
	if (c >= Min) return 100;// 劈瓜的次数大于等于最小值
	if (sum == m) {
        Min = c;
        return c;
    }
    if (sum > m) return 100;// 如果当前sum大于m,提前结束
	if (i == n) {
        return 100; //此时已经使用了所有西瓜,也无法满足,直接排除掉
   }
    if (arr[i] + sum < m) return 100; // 如果当前sum加上剩余所有值都小于m,可提前结束
    int a = dfs(i + 1, sum + nums[i], c); // 全拿走 
    int b = dfs(i + 1, sum + (nums[i] / 2.0), c + 1); // 拿走一半 
    int f = dfs(i + 1, sum, c);  // 不拿走 
	int k = min(b, f);
    return min(a, k);
}

int main(){
        cin>>n>>m;
        int i = 0;
        for (i = 0; i < n; i++) {
            cin>>nums[i];
        }
        for (i = n - 1; i >= 0; i--) {
            arr[i] = arr[i + 1] + nums[i];
        }
        int m = dfs(0, 0, 0);
        if (m == 100)cout<<"-1";
        else cout<<m;
        return 0;
}

1.3:蓝桥杯——分糖果

1.31 问题描述

        两种糖果分别有9个和16个,要全部分给7个小朋友,每个小朋友 得到的糖果总数最少为2个最多为5个,问有多少种不同的分法。糖 果必须全部分完。只要有其中一个小朋友在两种方案中分到的糖果不完全相同,这两种 方案就算作不同的方案。

1.32 解题思路

参考上述组合总和题的解题思路一,仅需注意其边界条件依旧递归中的变量参数即可

1.33代码实现
#include <iostream>
using namespace std;
int num=0,x=9,y=16;
 
 void dfs(int n,int x,int y){//x为小朋友编号,m,n为两种糖果剩余数量
  if(n>7){
    if(x==0&&y==0)num++; //当分到第八位小孩,也就是说前七位小孩已经分完了,此时两种糖果数量等于0则满足条件
    return;
  }
  
  for(int i=0;i<=x;i++)
    for(int j=0;j<=y;j++)
      if(i+j>=2&&i+j<=5)dfs(n+1,x-i,y-j);

}
int main()
{
  dfs(1,9,16);
  cout<<num;
  return 0;
}

2.总结

        对于实现深度优先搜索的两种思路,每种思路都适用于不同的场景。例如,在解决类似蓝桥杯上的买瓜问题时,每个瓜都有三种选择方式,这显而易见地适合于考虑每个变量的选取情况。这种思路更适用于解决组合总和等类似问题的思路二。而对于像蓝桥杯上的分糖果问题,则需要考虑每个孩子分到糖果的多种可能性,且每个孩子都必须分到一定份额的糖果。这导致在每一层递归函数中必须选择一组值的糖果分给一个孩子,因此这种思路更适用于解决组合总和等问题的思路一。

总的来说,深度优先搜索在处理不同情况的问题时,需要根据具体情况进行分析,只有深入理解其搜索原理,才能在解决问题时取得优势地位。

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值