DFS 深度优先搜索

深度优先搜索会走遍所有路径,并且每次走到死胡同就代表一条完整路径的形成,这就是说,深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法


例题1:

有n件物品,每件物品的重量为w[ i ],价值为c[ i ],现在需要选出若干件物品放入容量为V的背包中,使得物品重量和不超过V的前提下,物品的价值最大。(1\leqslant n\leqslant 20

分析:每件物品都有选或不选两种选择,这就是所谓的“岔道口”,而当物品的重量和超过了V,就会到达“死胡同”,需要返回最近的“岔道口”。

每次都要对物品进行选择,因此DFS函数的参数中必须记录当前处理的物品编号index,已选物品的总重量sumW和总价值sumC,于是DFS函数看起来是这个样子:

void DFS(int index, int sumW, int sumC) {...}

如果不选择index号物品,那么就进入DFS(index+1, sumW, sumC),如果选择index号物品,将前往DFS(index+1, sumW+w[index], sumC+c[index] )这条分支。当选择物品的Index增长到了n,就说明已经把n件物品处理完毕,此时比较sumW,如果不超过V且sumC超过maxvalue,则更新maxvalue,下面为实现代码:

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

int n,V,maxvalue=0;  //n件物品,背包容量V,最大价值maxvalue
int w[30],c[30];     //每件物品的重量和价值

void DFS(int index, int sumW, int sumC)
{
    if(index==n){    //已处理完所有物品,物品下标为0到n-1
        if(sumW<=V && sumC>maxvalue){
            maxvalue = sumC;
        }
        return;
    }
    DFS(index+1, sumW, sumC); //不选第index件物品
    DFS(index+1, sumW+w[index], sumC+c[index]); //选index件物品
}

int main()
{
    cin>>n>>V;
    for(int i=0;i<n;i++) cin>>w[i];
    for(int i=0;i<n;i++) cin>>c[i];
    DFS(0,0,0);
    cout<<"maxvalue: "<<maxvalue<<endl;
    return 0;
}

由于每次需要处理完所有的物品再判断重量和价值,故复杂度为O(n^{2}),事实上忽视了物品重量不能超过V这个特点,可以将这个条件的判断加入“岔道口”中,只有sumW不超过V才能继续加入物品,DFS函数代码如下:

void DFS(int index, int sumW, int sumC)
{
    if(index==n) return;
    DFS(index+1, sumW, sumC ); //不选index号物品
    if(sumW+w[index]<=V){
        if(sumC+c[index] > maxvalue) maxvalue=sumC+c[index];
        DFS(index+1, sumW+w[index], sumC+c[index]);
    }
}

原来第二条岔路是直接进入的,而现在要判断加入第index件物品时重量不超过V才进入,这样可以减少计算量,这种通过题目条件的限制来节省DFS计算量的方法称作剪枝

这个问题也可以看作一类常用的DFS问题:给定一个序列,枚举这个序列的所有子序列 ,从中选择最优的子序列。


例题2:

给定n个整数,从中选择k个数,使得这k个数的和恰好等于一个给定的整数x,如果有多种方

案,选择它们中元素平方和最大的一个,数据保证这样的方案唯一。

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

int n,k,x,maxsqu=-1,A[30];

vector<int> temp,ans; //temp存放临时方案,ans存放平方和最大的方案

void DFS(int index,int nowk,int sum,int sumsqu)
{
    if(nowk==k && sum==x){  //满足选择k个整数且和为x
        if(sumsqu>maxsqu) {
            maxsqu = sumsqu;
            ans = temp;
        }
        return;
    }
    if(index==n||nowk>k||sum>x) return; //此时应返回
    temp.push_back(A[index]);  //选择index号数
    DFS(index+1, nowk+1, sum+A[index], sumsqu+A[index]*A[index]);
    temp.pop_back();   //将index号数从temp中去除,使之不会影响“不选 
                       //index号数”这条分支
    DFS(index+1, nowk, sum, sumsqu); 
}


int main()
{
    cin>>n>>k>>x;
    for(int i=0;i<n;i++) cin>>A[i];
    DFS(0,0,0,0);
    cout<<maxsqu<<endl;
    for(vector<int>::iterator it=ans.begin();it!=ans.end();it++)
        cout<<*it<<" ";
    return 0;
}

 上面这个问题每个数都只能选择一次,现在稍微修改题目:假设n个整数中的每一个都可以被选择多次,那么选择k个数,使得k个数的和恰为x。 那么当选择了Index号数时不应直接进入Index+1号数的选择,而应继续选择index号数,直到某个时刻决定不再选择index号数,就会进入Index+1号数的处理。因此只要把选择Index这条分支的代码修改为:

DFS(index, nowk+1, sum+A[index], sumsqu+A[index]*A[index]);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值