搜索——深度优先搜索(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。      

  代码如下:

 1 /*
 2     搜索_DFS
 3     有 n 件物品,每件物品的重量为 w[i],价值为 c[i]。现在需要选出若干件物品
 4         放入一个容量为 V 的背包中,使得在选入背包的物品重量和不超过容量 V 的前提下,
 5         让背包中物品的价值之和最大,求最大价值。(1≤n≤20)
 6 */
 7
 8 #include <stdio.h>
 9 #include <string.h>
10 #include <math.h>
11 #include <stdlib.h>
12 #include <time.h>
13 #include <stdbool.h>
14
15 #define maxn 30
16 int n, V, maxValue;    // 物品减数,背包容量,最大价值
17 int w[maxn], c[maxn];    // 每件物品的重量,价值
18
19 // index 当前处理的物品编号,sumW 和 sumC 为当前总重量和总价值
20 void DFS(int index, int sumW, int sumC) {
21     if(index == n) {    // 已经把 n 件物品处理完毕(死胡同)
22         if(sumW <= V && sumC > maxValue) {
23             maxValue = sumC;    // 有更好的选择
24         }
25         return;
26     }
27     // 岔道口
28     DFS(index+1, sumW, sumC);    // 不选 Index 号物品
29     DFS(index+1, sumW+w[index], sumC+c[index]);    // 选 index 号物品
30 }
31
32 int main() {
33     scanf("%d %d", &n, &V);
34     int i;
35     for(i=0; i<n; ++i) {
36         scanf("%d", &w[i]);        // 每件物品的重量
37     }
38     for(i=0; i<n; ++i) {
39         scanf("%d", &c[i]);        // 每件物品的价值
40     }
41     DFS(0, 0, 0);
42     printf("%d\n", maxValue);
43
44     return 0;
45 }

  在上述代码中,总是把 n 件物品的选择全部确定之后才去更新最大价值,但是事实上忽视了背包容量不超过 V 这个特点。也就是说,完全可以把对 sumW 的判断加入“岔道口”中,只有当 sumW ≤ V 时才进入岔道,这样效率会高很多。代码如下:

 1 // index 当前处理的物品编号,sumW 和 sumC 为当前总重量和总价值
 2 void DFS1(int index, int sumW, int sumC) {
 3     if(index == n) {    // 已经把 n 件物品处理完毕(死胡同)
 4         return;
 5     }
 6     // 岔道口
 7     DFS(index+1, sumW, sumC);    // 不选 Index 号物品
 8     // 只有加入 index 物品后总重量小于 V 才可以继续
 9     if(sumW + w[index] <= V) {
10         if(sumC + c[index] > maxValue) {
11             maxValue = sumC + c[index];
12         }
13         DFS(index+1, sumW+w[index], sumC+c[index]);    // 选 Index 号物品
14     }
15 }

  再来看另外一个问题。

  给定 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。  

  代码如下:

 1 /*
 2     DFS_N个整数选K个
 3     给定 N 个整数(可能有负数),从中选择 K 个数,使得这 K 个数之和
 4     恰好等于一个给定的整数 X;如果有多种方案,选择它们中元素平方和最大的一个。
 5 */
 6
 7 #include <stdio.h>
 8 #include <string.h>
 9 #include <math.h>
10 #include <stdlib.h>
11 #include <time.h>
12 #include <stdbool.h>
13
14 #define maxn 30
15 // 序列A中n个数选k个数使得和为x,最大平方和为maxSumSqu
16 int n, k, x, maxSumSqu=-1, A[maxn];
17 int temp[maxn]={0}, ans[maxn]={0};    // 临时方案,平方和最大的方案
18
19 void DFS(int index, int nowK, int sum, int sumSqu) {
20     if(nowK == k && sum == x) {        // 找到K个数和为x
21         if(sumSqu > maxSumSqu) {    // 更优方案
22             maxSumSqu = sumSqu;        // 更新 maxValue 和数组 ans
23             int i;
24             for(i=0; i<k; ++i) {
25                 ans[i] = temp[i];
26             }
27         }
28     }
29     // 已经处理完n个数,选择超过k个数,和大于x
30     if(index==n || nowK>k || sum>x)    return;
31     // 选 index 号数
32     temp[nowK] = A[index];
33     DFS(index+1, nowK+1, sum+A[index], sumSqu+A[index]*A[index]);
34     temp[nowK] = 0;
35     // 不选 index 号数
36     DFS(index+1, nowK, sum, sumSqu);
37 }
38
39 int main() {
40     scanf("%d %d %d", &n, &k, &x);
41     int i;
42     for(i=0; i<n; ++i) {
43         scanf("%d", &A[i]);    // n个数
44     }
45     DFS(0, 0, 0, 0);
46     for(i=0; i<k; ++i) {    // 最优方案
47         printf("%d ", ans[i]);
48     }
49     printf("%d\n", maxSumSqu);    // 最优方案的平方和
50
51     return 0;
52 }

原文地址:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes19.html

时间: 2024-11-08 11:39:46

搜索——深度优先搜索(DFS)的相关文章

POJ 2196 Computer(搜索-深度优先搜索)

Computer Problem Description A school bought the first computer some time ago(so this computer's id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are

挑战程序2.1.4 穷竭搜索&gt;&gt;深度优先搜索

{'W','.','.','W','.'}, {'W','.','.','.','.'}, {'.','.','.','W','.'}, {'W','.','.','.','.'}, {'W','.','W','W','W'}例子,此处有5处积水 题目:有N*M格子的花园,W表示积水,'.'表示干地,积水附近8个区域只要有积水,那么这两处连通,并算作是同一处积水,问一共有几处积水? 思路:穷竭搜索,O(N*M),搜索到积水则变成'.',并搜索连通的积水将其变成'.',直到这块积水搜索结束,此时a

挑战程序2.1.4 穷竭搜索&gt;&gt;深度优先搜索 练习题 POJ1979黑与红

http://poj.org/problem?id=1979 Description There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move

【图论】广度优先搜索和深度优先搜索

写在最前面的 这篇文章并没有非常详细的算法证明过程.导论里面有非常详细的证明过程.本文只阐述“广度优先和深度优先搜索的思路以及一些简单应用”. 两种图的遍历算法在其他图的算法当中都有应用,并且是基本的图论算法. 广度优先搜索 广度优先搜索(BFS),可以被形象的描述为“浅尝辄止”,具体一点就是每个顶点只访问它的邻接节点(如果它的邻接节点没有被访问)并且记录这个邻接节点,当访问完它的邻接节点之后就结束这个顶点的访问. 广度优先用到了“先进先出”队列,通过这个队列来存储第一次发现的节点,以便下一次的

人员分配问题(简单的深度优先搜索题目)

问题描述 某单位有若干人员,现执行某任务需要一定人数人员.编写程序,输入单位人数和每位员工名字,再输入执行任务所需人数,输出所有可能派出人员方案 程序代码 #include<bits/stdc++.h> using namespace std; char s[10][20]; //存放读进来的人的名字,暂定人最多为10个 char ans[10][20]; //存放每一次生成的结果 int maxMan = 0; //存放执行任务所需人数 int times = 0; //存放总方案数 int

算法-无向图(深度优先搜索和广度优先搜索)

图中最常用到的两种搜索深度优先搜索和广度优先搜索,深度优先搜索是一种在开发爬虫早期使用较多的方法它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链接的Html文件) ,广度搜索属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果.换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止. 深度优先搜索 图中我们经常会遇到一个问题就是图的连通性,比如说从一个顶点到另外一个顶点,判断顶点和其他顶点之间的连通性,以下图为例: 搜索API定义: @interface

深度优先搜索(dfs)

关于深度优先搜索的总结: 1 dfs 的基本结构:  void dfs(int x){ if( x 超出边界){ return ; }else{ for(遍历){ if(未访问过){ 访问         ; 打上标记    ; dfs(x + 1) ; 去掉标记    ; //极易忘记 } } } return; } 2 用dfs求全排列: 本来好好的,结果sizeof(pointer) 就完蛋了.神秘的内存错误,而且还能正常的跑出一个不正常的结果出来. 想了解sizeof这个小妖精的看这里

深度优先搜索(DFS)

定义: (维基百科:https://en.wikipedia.org/wiki/Depth-first_search) 深度优先搜索算法(Depth-First-Search),是搜索算法的一种.是沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点.这一过程一直进行到已发现从源节点可达的所有节点为止.如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止(属于盲目搜索). 基

图的遍历之深度优先搜索(DFS)

深度优先搜索(depth-first search)是对先序遍历(preorder traversal)的推广.”深度优先搜索“,顾名思义就是尽可能深的搜索一个图.想象你是身处一个迷宫的入口,迷宫中的路每一个拐点有一盏灯是亮着的,你的任务是将所有灯熄灭,按照DFS的做法如下: 1. 熄灭你当前所在的拐点的灯 2. 任选一条路向前(深处)走,每经过一个拐点将灯熄灭直到与之相邻的拐点的灯全部熄灭后,原路返回到某个拐点的相邻拐点灯是亮着的,走到灯亮的拐点,重复执行步骤1 3. 当所有灯熄灭时,结束 将