搜索——深度优先搜索(DFS)
设想我们现在身处一个巨大的迷宫中,我们只能自己想办法走出去,下面是一种看上去很盲目但实际上会很有效的方法。
以当前所在位置为起点,沿着一条路向前走,当碰到岔道口时,选择其中一个岔路前进。如果选择的这个岔路前方是一条死路,就退回到这个岔道口,选择另一个岔路前进。如果岔路口存在新的岔道口,那么仍然按上面的方法枚举新岔道口的每一条岔道。这样,只要迷宫存在出口,那么这个方法一定能够找到它。
也就是说,当碰到岔道口时,总是以“深度”作为前进的关键词,不碰到死胡同就不回头,因此这种搜索的方式称为深度优先搜索(DFS)。
接下来讲解一个例子。
有 n 件物品,每件物品的重量为 w[i],价值为 c[i]。现在需要选出若干件物品放入一个容量为 V 的背包中,使得在选入背包的物品重量和不超过容量 V 的前提下,让背包中物品的价值之和最大,求最大价值。(1≤n≤20)
在这个问题中,对每件物品都有选或者不选两种选择,而这就是所谓的“岔道口”。那么什么是“死胡同”呢?题目要求选择的物品重量总和不能超过 V,因此一旦选择的物品重量总和超过 V,就会到达“死胡同”,需要返回最近的“岔道口”。
DFS 函数的参数中必须记录当前处理的物品编号 index,和在处理当前物品之前,已选物品的总重量 sumW 与 总价值 sumC。于是 DFS 函数如下:
void DFS(int index, int sumW, int sumC) {...}
思路:
- 如果选择不放入 index 号物品,那么 sumW 与 sumC 就将不变,接下来处理 index+1 号物品,即前往 DFS(index+1, sumW, sumC) 这条分支;
- 如果选择放入 index 号物品,那么 sumW=sumW+w[index], sumC=sumC+c[index],接着处理 index+1 号物品,即前往 DFS(index+1, sumW+w[index], sumC+c[index]) 这条分支;
- 一旦 index 增长到了 n,则说明已经把 n 件物品处理完毕。此时记录的 sumW 和 sumC 就是所选物品的总重量和总价值。如果 sumW 不超过 V 且 sumC 大于记录的最大总价值 maxValue,就说明当前的这种选择方案可以得到更大的价值,于是用 sumC 更新 maxValue。
代码如下:
/*
搜索_DFS
有 n 件物品,每件物品的重量为 w[i],价值为 c[i]。现在需要选出若干件物品
放入一个容量为 V 的背包中,使得在选入背包的物品重量和不超过容量 V 的前提下,
让背包中物品的价值之和最大,求最大价值。(1≤n≤20)
*/ #include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h> #define maxn 30
int n, V, maxValue; // 物品减数,背包容量,最大价值
int w[maxn], c[maxn]; // 每件物品的重量,价值 // index 当前处理的物品编号,sumW 和 sumC 为当前总重量和总价值
void DFS(int index, int sumW, int sumC) {
if(index == n) { // 已经把 n 件物品处理完毕(死胡同)
if(sumW <= V && sumC > maxValue) {
maxValue = sumC; // 有更好的选择
}
return;
}
// 岔道口
DFS(index+, sumW, sumC); // 不选 Index 号物品
DFS(index+, sumW+w[index], sumC+c[index]); // 选 index 号物品
} int main() {
scanf("%d %d", &n, &V);
int i;
for(i=; i<n; ++i) {
scanf("%d", &w[i]); // 每件物品的重量
}
for(i=; i<n; ++i) {
scanf("%d", &c[i]); // 每件物品的价值
}
DFS(, , );
printf("%d\n", maxValue); return ;
}
在上述代码中,总是把 n 件物品的选择全部确定之后才去更新最大价值,但是事实上忽视了背包容量不超过 V 这个特点。也就是说,完全可以把对 sumW 的判断加入“岔道口”中,只有当 sumW ≤ V 时才进入岔道,这样效率会高很多。代码如下:
// index 当前处理的物品编号,sumW 和 sumC 为当前总重量和总价值
void DFS1(int index, int sumW, int sumC) {
if(index == n) { // 已经把 n 件物品处理完毕(死胡同)
return;
}
// 岔道口
DFS(index+, sumW, sumC); // 不选 Index 号物品
// 只有加入 index 物品后总重量小于 V 才可以继续
if(sumW + w[index] <= V) {
if(sumC + c[index] > maxValue) {
maxValue = sumC + c[index];
}
DFS(index+, sumW+w[index], sumC+c[index]); // 选 Index 号物品
}
}
再来看另外一个问题。
给定 N 个整数(可能有负数),从中选择 K 个数,使得这 K 个数之和恰好等于一个给定的整数 X;如果有多种方案,选择它们中元素平方和最大的一个。
与之前的问题类似,此处仍然需要记录当前处理的整数编号 index;由于要求恰好选择 K 个数,因此需要一个参数 nowK 来记录当前已经选择的数的个数;另外,还需要参数 sum 和 sumSqu 分别记录当前已选整数之和与平方和。于是 DFS 函数如下:
void DFS(int index, int nowK, int sum, int sumSqu) {...}
思路:
- 需要一个数组 temp,用以存放当前选择的整数。
- 当试图进入“选 index 号数”这条分支时,就把 A[index] 加入 temp 中;
- 当这条分支结束时,就还原 temp 数组,使他不影响“不选 index 号数”这条分支。
- 如果当前已选择了 K 个数,且这 K 个数之和恰为 x 时,就将平方和与已有的最大平方和 maxValue 作比较,如果更大,更新 maxValue 和数组 ans。
代码如下:
/*
DFS_N个整数选K个
给定 N 个整数(可能有负数),从中选择 K 个数,使得这 K 个数之和
恰好等于一个给定的整数 X;如果有多种方案,选择它们中元素平方和最大的一个。
*/ #include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h> #define maxn 30
// 序列A中n个数选k个数使得和为x,最大平方和为maxSumSqu
int n, k, x, maxSumSqu=-, A[maxn];
int temp[maxn]={}, ans[maxn]={}; // 临时方案,平方和最大的方案 void DFS(int index, int nowK, int sum, int sumSqu) {
if(nowK == k && sum == x) { // 找到K个数和为x
if(sumSqu > maxSumSqu) { // 更优方案
maxSumSqu = sumSqu; // 更新 maxValue 和数组 ans
int i;
for(i=; i<k; ++i) {
ans[i] = temp[i];
}
}
}
// 已经处理完n个数,选择超过k个数,和大于x
if(index==n || nowK>k || sum>x) return;
// 选 index 号数
temp[nowK] = A[index];
DFS(index+, nowK+, sum+A[index], sumSqu+A[index]*A[index]);
temp[nowK] = ;
// 不选 index 号数
DFS(index+, nowK, sum, sumSqu);
} int main() {
scanf("%d %d %d", &n, &k, &x);
int i;
for(i=; i<n; ++i) {
scanf("%d", &A[i]); // n个数
}
DFS(, , , );
for(i=; i<k; ++i) { // 最优方案
printf("%d ", ans[i]);
}
printf("%d\n", maxSumSqu); // 最优方案的平方和 return ;
}
搜索——深度优先搜索(DFS)的更多相关文章
- 挑战程序2.1.4 穷竭搜索>>深度优先搜索
深度优先搜索DFS,从最开始状态出发,遍历一种状态到底,再回溯搜索第二种. 题目:POJ2386 思路:(⊙v⊙)嗯 和例题同理啊,从@开始,搜索到所有可以走到的地方,把那里改为一个值(@或者 ...
- 常用算法2 - 广度优先搜索 & 深度优先搜索 (python实现)
1. 图 定义:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合. 简单点的说:图由节点和边组成.一 ...
- python实现广度优先搜索和深度优先搜索
图的概念 图表示的是多点之间的连接关系,由节点和边组成.类型分为有向图,无向图,加权图等,任何问题只要能抽象为图,那么就可以应用相应的图算法. 用字典来表示图 这里我们以有向图举例,有向图的邻居节点是 ...
- 图的遍历(搜索)算法(深度优先算法DFS和广度优先算法BFS)
图的遍历的定义: 从图的某个顶点出发访问遍图中所有顶点,且每个顶点仅被访问一次.(连通图与非连通图) 深度优先遍历(DFS): 1.访问指定的起始顶点: 2.若当前访问的顶点的邻接顶点有未被访问的,则 ...
- 深度优先搜索(DFS)
[算法入门] 郭志伟@SYSU:raphealguo(at)qq.com 2012/05/12 1.前言 深度优先搜索(缩写DFS)有点类似广度优先搜索,也是对一个连通图进行遍历的算法.它的思想是从一 ...
- 深度优先搜索(DFS)
定义: (维基百科:https://en.wikipedia.org/wiki/Depth-first_search) 深度优先搜索算法(Depth-First-Search),是搜索算法的一种.是沿 ...
- 图的遍历之深度优先搜索(DFS)
深度优先搜索(depth-first search)是对先序遍历(preorder traversal)的推广.”深度优先搜索“,顾名思义就是尽可能深的搜索一个图.想象你是身处一个迷宫的入口,迷宫中的 ...
- HDU 1241 Oil Deposits DFS(深度优先搜索) 和 BFS(广度优先搜索)
Oil Deposits Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total ...
- 算法总结—深度优先搜索DFS
深度优先搜索(DFS) 往往利用递归函数实现(隐式地使用栈). 深度优先从最开始的状态出发,遍历所有可以到达的状态.由此可以对所有的状态进行操作,或列举出所有的状态. 1.poj2386 Lake C ...
随机推荐
- javascript: what can javascript do?
1.Javascript can change html content <!DOCTYPE html> <html> <body> <h2>What ...
- 剑指offer--34.数字在排序数组中出现的次数
时间限制:1秒 空间限制:32768K 热度指数:209611 本题知识点: 数组 题目描述 统计一个数字在排序数组中出现的次数. class Solution { public: int GetNu ...
- 2017.11.10 MPLAB IPE + ICD-3+ PIC32MM
A trouble with ICD-3 programmer. MCU: PIC32MM sw: MPLAB IPE tool: ICD-3 1 product introduction a ...
- Android application testing with the Android test framework
目录(?)[-] Android automated testing 1 How to test Android applications Tip 2 Unit tests vs functional ...
- 高级C/C++编译技术之读书笔记(五)之动态库版本控制
最近有幸阅读了<高级C/C++编译技术>深受启发,该书深入浅出地讲解了构建过程(编译.链接)中的各种细节,从多个角度展示了程序与库文件或代码的集成方法,提出了面向代码复用和系统集成的软件架 ...
- MyEclipse2014安装操作步骤+破解
第一步 第二步 第三步 第四步 第五步 第六步 破解操作步骤 1.安装完成 MyEclipse2014(适用于 2013 等版本)后,不要打开软件.解压破解文件压缩包,得到一下文件列表:双击 run. ...
- asp.net core microservices 架构之 分布式自动计算(二)
一 简介 上一篇介绍了zookeeper如何进行分布式协调,这次主要讲解quartz使用zookeeper进行分布式计算,因为上一篇只是讲解原理,而这次实际使用, ...
- vc++ windows 开始菜单添加快捷方式
开始菜单创建快捷方式 在windows软件开发中,软件安装过程中总是需要在开始菜单创建快捷方式,下面介绍一种开始菜单创建快捷方式的方法,具体代码如下: /* * 创建快捷方式 * szExePath[ ...
- push()、shift()与pop()、unshift()、splice()
1.末端的添加和移除:push()是用来在数组末端添加项,pop()在数组末端移除项: 2.前端的添加和移除:shift()在移除数组的第一个项(前端),unshift()在数组前端添加项: 3.pu ...
- elasticsearch 动态模板
在elasticsearch中,如果你有一类相似的数据字段,想要统一设置其映射,就可以用到一项功能:动态模板映射(dynamic_templates). 每个模板都有一个名字用于描述这个模板的用途,一 ...