算法笔记搜索之DFS

算法笔记之DFS
我们可以从下面这个图进行深入的理解:
在这里插入图片描述
从起点开始,当碰到岔道口时,总会随机选择其中的一条岔道口前进,如果在路上又碰到新的岔道口再次随机选择一条新的岔道口,直到遇到死胡同在返回到最近的一条新岔道口进行重新选择。直至遇到出口为止。
这里有两个重要的点:岔道口和死胡同(我也叫它递归边界或剪枝)
总而言之:DFS是一种枚举所有完整路径以遍历所有情况的搜索方法。
在枚举过程中寻找某种符合题目要求的方案。
以下通过几个例子加深理解:

  1. n个物品,即每种物品一个 重量w[i],价值c[i] 选入容量为V的背包,求其最大价值。【n<=20】
    这可以看成一到道搜索题,当然贪心也可以做(按照c/w 从大到小排序,复杂度大概是O(logn) )
    岔道口:第i件物品是否选择 选? 不选?
    死胡同:a、已经判断过第n个物品是否选择了
    b、当前所选重量>背包重量 ==> 只有在now_v<V 时才能选择第1个岔路 口, 即选择,这两步称为剪枝
    Note: 可以定义一个全局变量 MAX_C来记录最大价值

#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<ctime>
#include<algorithm>
#include<iterator>
#include<vector>
#include<stack>
#include<set>
#include<queue>
#include<map>
#include<utility>
using namespace std;
const int N=21;
int w[N];
int c[N];
int MAX_C=0;
int n,V;
//index 当前处理的物品编号, 当前重量,当前价值
void dfs(int index,int now_v,int now_c){
    if(index==n)
    return ;

    //不选第Index 件物品
    dfs(index+1,now_v,now_c);

    //只有在now_v<V 时才能选择第1个岔路口,即选择
    if(now_v+w[index]<=V){
        if(now_c+c[index]>MAX_C){
            MAX_C=now_c+c[index];
        }
        dfs(index+1,now_v+w[index],now_c+c[index]);
    }

}
int main()
{
	ios::sync_with_stdio(false);
    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<<MAX_C<<endl;
    system("pause");
	return 0;
}
/*
5 8
3 5 1 2 2
4 5 2 1 3
 */

2

/*
枚举所有子序列的目的很明显,可以从中选择一个"最优子序列",使得它的某个特征是所有子序列中最优的
显然,这个问题也等价于枚举从n个整数中选择K个数的所有方案。
例题:从n个数里选出k个数,使得这k个数的和为x,并且要求这k个数的平方和最大
      每个数仅仅可以被选择1次
输入:n k x
    a[0] a[1] a[2]....a[n-1]
输出: 最优子序列
input:
4 2 6
2 3 3 4
output:
2 4

Notes:
    面对岔道口有选择a[index]和不选a[index两种方案。
    当选择a[index]时就要temp.push_back(a[index]),而当这条分支结束时,就要temp.pop_back()
    即将它从temp中去除,使它不会影响”不选a[index]“这条分支
 */
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<ctime>
#include<algorithm>
#include<iterator>
#include<vector>
#include<stack>
#include<set>
#include<queue>
#include<map>
#include<utility>
using namespace std;
const int N=50;
int n,k,x;
int a[N];
int max_squsum=0;
//temp存放临时方案,ans存放最优方案
vector<int> temp,ans;

//index 当前处理index号整数 nowk 当前已经选数的个数
//sum 当前已经选的数之和 当前已选数平方和
//该dfs() 是在数组a中所有的数只能选择一次的情况下
void dfs(int index,int nowk,int sum,int squsum){
    //注意分岔口和死胡同
    if(nowk==k && sum==x){
        if(squsum>max_squsum){
            max_squsum=squsum;  //更新最大平方和
            ans=temp;   //更新最优方案
        }
        return ;
    }
    if(index==n||nowk>k||sum>x){
        return ;
    }
    //选a[index]
    temp.push_back(a[index]);
    dfs(index+1,nowk+1,sum+a[index],squsum+a[index]*a[index]);
    temp.pop_back();
    //不选a[index]
    dfs(index+1,nowk,sum,squsum);
}
int main()
{
	ios::sync_with_stdio(false);
    while(cin>>n>>k>>x){
        for(int i=0;i<n;i++)
        cin>>a[i];

        dfs(0,0,0,0);
        for(vector<int>::iterator it=ans.begin();it!=ans.end();it++)
        cout<<*it<<" ";
        cout<<endl;
        cout<<"max_squsum is:"<<max_squsum<<endl;
    }
    system("pause");
	return 0;
}

3、第2题的延申

/*
例题:从n个数里选出k个数,使得这k个数的和为x,并且要求这k个数的平方和最大,每个数都可以被选择多次
输入:n k x
    a[0] a[1] a[2]....a[n-1]
输出: 最优子序列
input:  从3个数{1,4,7}中选出5个数,使得这5个数和为17且平方和最大
3 5 17
1 4 7
output:
1 1 1 7 7

Notes:
    这个问题可由上面的例题改动即可。由于每个数可以被选择多次,因此在岔道口选择了a[index]时
    不因该进入(index+1)号数进行处理,应当能够继续选择a[index]。
    当然不选a[index]则正常进入(index+1)号数进行处理。
 */
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdio>
#include<ctime>
#include<algorithm>
#include<iterator>
#include<vector>
#include<stack>
#include<set>
#include<queue>
#include<map>
#include<utility>
using namespace std;
const int N=50;
int n,k,x;
int a[N];
int max_squsum=0;
//temp存放临时方案,ans存放最优方案
vector<int> temp,ans;

//index 当前处理index号整数 nowk 当前已经选数的个数
//sum 当前已经选的数之和 当前已选数平方和
//该dfs() 是在数组a中所有的数只能选择一次的情况下
void dfs(int index,int nowk,int sum,int squsum){
    //注意分岔口和死胡同
    if(nowk==k && sum==x){
        if(squsum>max_squsum){
            max_squsum=squsum;  //更新最大平方和
            ans=temp;   //更新最优方案
        }
        return ;
    }
    if(index==n||nowk>k||sum>x){
        return ;
    }
    //选a[index]
    temp.push_back(a[index]);
    dfs(index,nowk+1,sum+a[index],squsum+a[index]*a[index]);    //!!不同之处!!!!!!!
    temp.pop_back();
    //不选a[index]
    dfs(index+1,nowk,sum,squsum);
}
int main()
{
	ios::sync_with_stdio(false);
    while(cin>>n>>k>>x){
        for(int i=0;i<n;i++)
        cin>>a[i];

        dfs(0,0,0,0);
        for(vector<int>::iterator it=ans.begin();it!=ans.end();it++)
        cout<<*it<<" ";
        cout<<endl;
        cout<<"max_squsum is:"<<max_squsum<<endl;
    }
    system("pause");
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值