再谈DFS(深度优先遍历)(集合求所有子集问题、求最长路径问题、如何保存最优化问题的结果)

DFS 是“深度优先遍历”或“深度优先搜索”的英文缩写。一开始是用于解决“迷宫问题”,或者说是“连通性问题”所产生的,用于判断两点间是否可达。对于求最短路径一般用的是BFS(广度优先搜索),不过现在从DFS衍生出了好几种问题的解决,比如题中给出的“集合求所有子集问题、”求最长路径问题”、“如何保存最优化问题的结果”这三个问题。

首先还是先从经典的迷宫问题引入DFS。要判断u和v是否连通,我们可以先挑出其中一个点,例如u。用一个函数去查找u所有的“下一跳”。(“下一跳”源自于TCP/IP Routing and Switching中用来指代路由器或者交换机的出接口连接的下一个路由器或交换机,也就是报文传送的下一个可能的地点。)这个查找函数或者查找操作称为称为ADT。最后将所有可以连通的点都遍历完,判断v是否在这些可连通的点组成的集合里。在的话说明是连通的,反之说明不连通。


这里给出源自于斯坦福的教材《算法:C语言实现》(《Algorithms THRD EDITION in C》)的示例图:


在讲“求最长路径问题”问题时,用这么一个例子引入:

有n件物品,每件物品的重量为w[i],价值为c[i]。现在需要选出若干件物品放入一个容器大小为V的背包中,使得在入选背包的物品重量和不超过背包容量V的前提下,让背包中物品的价格之和最大,求其最大值。(1<=n<=20)

在这个问题中,需要从n件物品中选择若干件物品放入背包,使它们的价值之和最大。这样的话,对每件物品都有“选择”和“不选”两种,这可以看作迷宫的"岔路口"。当要求物品重量大于V时,即进入迷宫的“死胡同”。于是传化为一个“迷宫问题了”,只是这回要求所有存放物品的最大价值(即“路径的最大值”)。

显然,每次都要对物品选择,所以DFS得传入一个记录物品编号的参数,记为index,使用int整型即可。还有就是所涉及当前已选物品的总价值sumC和总重量sumW。代码如下:

#include<iostream>
using namespace std;

const int maxn = 30;
int n, v, maxValue = 0;//物品件数为n,背包容量为v,最大价值为maxValue,
//w[i]是用来存放编号I的物品重量的数组,c[i]是用来存放编号I的物品价值的数组
int w[maxn], c[maxn];

//index是当前处理的物品的编号
//sunW和sumC是当前背包里物品的总重量和总价值。
void DFS(int index, int sumW, int sumC)
{
	if (index == n)//已经对n件物品完成选择(进入死胡同)
	{
		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(void)
{
	//初始化相关信息
	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 << endl;
	system("pause");
	return 0;
}

实际上,还可以这么理解:这是一类常见的DFS问题的应用,即“集合求所有子集问题”。将所有子集(满足条件的物品组合)求出来,再进行比较求最大值(元素之和最大的集合)。DFS()函数代码可以这么改,来简化复杂度,在示例一的基础上修改:

void DFS(int index, int sumW, int sumC)
{
	//已经对n件物品完成选择(进入死胡同)
	if (index == n)return;

	//进入岔道口    
	DFS(index + 1, sumW, sumC);//不选择编号为index的物品

	//加入编号为index的物品后未超过容量v,才继续这一步
	if (sumW + w[index] <= v)
	{
		if (sumC + c[index] > maxValue)
			maxValue = sumC + c[index];
		DFS(index + 1, sumW + w[index], sumC + c[index]);
	}
}

最后是如何解决最优值的存放,可以像刚才那样定义全局变量,例如maxValue来解决问题,但是也可以传入DFS()函数中,以求最大的平方和,记为maxSumSqu为例,仍是在示例一的基础上修改,示例如下:

#include<vector>
//A[index]是一个散列,index对应当前的权重
int n, k, x, maxSumSqu = -1, A[maxn];
//temp存放临时方案,ans存放最大平方和方案
vector<int>temp, ans;

//处理当前index号的数,当前已选整数有noeK个
//当前已选整数之和为sum,档期那已选整数平方和为sumSqu
void DFS(int index, int nowK, int sum, int sumSqu)
{
	if (nowK == k && sum == x)
	{
		//寻找更优
		if (sumSqu > maxSumSqu)
		{
			maxSumSqu = sumSqu;//更新最大平方和
			ans = temp;//更新最优方案
		}
		return;
	}

	//已经处理完n个数。或者超过n个数,返回。
	if (index == n || nowK > k || sum > x)return;

	//选编号为index的数
	temp.push_back(A[index]);
	DFS(index + 1, nowK + 1, sum + A[index], sumSqu + A[index] * A[index]);
	temp.pop_back();

	//不选编号为index的数
	DFS(index + 1, nowK, sum, sumSqu);
}

END

发布了162 篇原创文章 · 获赞 38 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_41938259/article/details/104074363