深度优先搜索总结

概览:可用"搜索"思想来解决的问题一般来说分为两类,一类是像树、图这样有明确数据结构的,这一类理解起来比较直观,搜索过程即一层一层直到最深处;对于隐式图,就没有明确的数据结构,需要根据题意确定状态变化对应的参数,这里先讲后面一种情况。

一、对于没有明确数据结构的深搜

【例题1】有n(1<=n<=20)件物品,每件物品的重量为w[i],价值为v[i]。现在需要选出若干件物品放入一个容量为V的背包中,使得在选入背包的物品重量之和不超过totWeight的前提下,让背包中的物品价值之和最大,求最大价值以及所选物品的编号(假设编号从0开始)。

分析:对于每件物品,都有选或不选两种可能,即dfs的两个分支。深搜过程中要记录当前所选物品的重量之和(记为sumWeight),以及当前物品的价值之和(记为sumValue),当然还有物品的编号idx,由此可基本确定dfs的接口为dfs(int idx, int sumWeight, int sumValue)。假设当前物品编号为idx,若选择该物品,则状态改变为sumWeight+w[idx]和sumValue+v[idx],然后进入下一个物品idx+1;若不选择该物品,则sumWeight和sumValue的状态不变,进入下一个物品idx+1。

输入数据:
5 8          //n件物品 容量V为8
3 5 1 2 4 //每件物品的重量
4 5 2 3 1 //每件物品的价值
输出结果:
10
1 2 3

代码:

#include <cstdio>
#include <vector>
using namespace std;

int w[20],v[20];
vector<int> ans,tmp;
int n, totWeight;//物品个数,背包最多可放入放入重量
int maxValue=0;

void dfs(int idx,int sumWeight,int sumValue)
{
    if(idx==n) return;//因为下标0~n-1,所以遍历到n时,说明已经遍历完了所有物品
    if(sumWeight+w[idx] <= totWeight){//若选择idx号物品不会超过背包最大承受重量,那就选择它
        tmp.push_back(idx);
        if(sumValue+v[idx] > maxValue){
            maxValue=sumValue+v[idx];
            ans=tmp;
        }
        dfs(idx+1,sumWeight+w[idx],sumValue+v[idx]);
        tmp.pop_back();
    }
    dfs(idx+1,sumWeight,sumValue);//不选择idx号物品
}

int main()
{
    scanf("%d%d",&n,&totWeight);
    for(int i=0;i<n;i++) scanf("%d",&w[i]);
    for(int i=0;i<n;i++) scanf("%d",&v[i]);
    dfs(0,0,0);
    printf("maxValue:%d\n",maxValue);
    for(int i=0;i<ans.size();i++){
        printf("%d ",ans[i]);
    }
    return 0;
}

【例题2】给出N个整数,从中选出K个数(K<=N,每个数最多只能选一次),使这K个数的和为S,若有多种方案则选择各元素的平方和最大的方案。如给出{2 3 3 4},要求从中选出2个数,使其和为6,则有{2 4}或{3 3},其中{2 4}的平方和更大,则其为最优解。

分析:这类问题可以抽象为 “从N个整数中选择K个整数,使这K个整数符合某一条件,求最优解”,这是一类DFS的典型问题。对于每个数i,都有选或不选两种可能,思路和例题1是一样的,只是DFS的接口会有不同,根据题目的限制条件来定。本题中,边界条件有:①达到K个整数,②K个整数和恰为S,③当前所选择的数的平方和currSquare,当然还有数组的下标idx,在满足了①②的情况下更新全局最优的maxSquare。

代码:

#include <cstdio>
#include <vector>
using namespace std;
const int n=4;
int data[n]={2,3,3,4};
vector<int> ans,tmp;
int k=2,sum=6;
int maxSquare=-1;

void dfs(int idx,int currK,int currSum,int currSquare)
{
    if(currK==k && currSum==sum){//符合某一条件
        if(currSquare>maxSquare){
            maxSquare=currSquare;
            ans=tmp;
        }
        return;
    }
    if(idx==n || currK>k || currSum>sum) return;
    tmp.push_back(data[idx]);
    dfs(idx+1,currK+1,currSum+data[idx],currSquare+data[idx]*data[idx]);//语句1
    tmp.pop_back();
    dfs(idx+1,currK,currSum,currSquare);
}

int main()
{
    dfs(0,0,0,0);
    for(auto it:ans) printf("%d ",it);//输出2,4
    return 0;
}
还是上面的题干,若可以对每个数字可选择多次,如有{1 4 7},要求从中选择5个数,使其和为17,则有{1 4 4 4 4}和{1 1 1 7 7}两种选法,但是后者的平方和更大,故为最优解。
只要改动一个地方即可,即当前选择第idx个数时,下一次选择仍然可能选idx,即把语句(1)改为  dfs(idx,currK+1,currSum+data[idx],currSquare+data[idx]*data[idx]); 。这在PAT中就出现过,回顾: 1103 Integer Factorization

二、对于树、图的深搜

这一类在PAT中就出现n回了,最简单的,比如说树的前序遍历本质上就是深搜,只是我们习惯上不这么叫罢了。更多的,DFS是利用在求最短路径当中,搭配Dijkstra一起使用,由Dijkstra求出最短路径,保存最短路径上各个结点的前驱,再由DFS来获取最优解;亦或,在树相关的题目中,比如求树的最大深度,求树从根结点到某一叶结点的简单路径(以满足某个条件)等等。如, 1106 Lowest Price in Supply Chain 1135 Is It A Red-Black Tree1021 Deepest Root等等;此外,若要求无权最短路径(相当于求起点到终点的边的数量,对于树形结构就是最大/小深度,对于图就是最短路径),也可以利用DFS来求解,如 1131 Subway Map 
 

转载于:https://www.cnblogs.com/kkmjy/p/9599686.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值