深度优先搜索
深度优先搜索:从起点开始,当碰到岔道口时,总是选择其中一条岔路前进,在岔路上如果又遇到新的岔道口,仍然选择新岔道口的其中一条岔路前进,直到碰到死胡同才回退到最近的岔道口选择另一条岔路。(不碰到死胡同就不回头)
深度优先搜索是一种枚举所有完整路径以遍历所有清空的搜索方法。
使用递归可以很好地实现深度优先搜索,系统会调用一个叫系统栈的东西来存放递归中每一层的状态,因此使用递归来实现DFS的本质还是栈。
例题
有n件物品,每件物品的重量为w[i],价值为c[i]。现在需要选出若干物件放入一个容量为V的背包中,使得在选入背包的物品重量和不超过容量V的前提下,让背包中物品的价值之和最大,求最大值(1<=n<=20)
分析
对每件物品都有选或者不选两种选择,而这就是所谓的“岔道口”。题目要求选择的物品重量总和不能超过V,一旦超过就会到达“死胡同”,需要返回最近的“岔道口”。
DFS函数的参数中必须记录当前处理的物品编号index,已选物品的总重量sumV与总价值sumC。
void DFS(int index, int sumW, int sumC){...}
如果选择不放入index号物品,那么sumW与sumC就将不变
接下来处理index+1号物品,即前往DFS(index+1, sumW, sumC)这条分支
如果选择放入index号物品,那么sumW将增加当前物品的重量w[index],sumC将增加当前物品的价值c[index]
接着处理index+1号物品,即前往DFS(index+1, sumW+w[index], sumC+c[index])这条分支
一旦index增长到n,则说明已经把n件物品处理完毕,此时记录的sumW和sumC就是所选物品的总重量和总价值。
如果sumV不超过V且sumC大于一个全局的记录最大总价值的变量maxValue,就说明当前方案可以得到更大价值,用sumC更新maxValue。
#include <cstdio>
const int maxn = 30;
int n, V, maxValue = 0; //物品件数n,背包容量V,最大价值maxValue
int w[maxn], c[maxn]; //w[i]为每件物品的重量,c[i]为每件物品的价值
//DFS,index为当前处理的物品编号
//sumW,sumC分别为当前总重量和当前总价值
void DFS(int index, int sumW, int sumC)
{
if(index == n) //已经完成对n件物品的选择(死胡同)
{
if(sumW <= V && sumC > maxValue)
{
maxValue = sumC; //不超过背包容量时更新最大价值maxValue
}
return;
}
//岔道口
DFS(index + 1, sumW, sumC); //不选第index件物品
DFS(index + 1, sumW + w[index], sumC + c[index]); //选第index件物品
}
int main()
{
scanf("%d%d", &n, &V);
for(int i = 0; i < n; i++)
{
scanf("%d", &w[i]); //每件物品的重量
}
for(int i = 0; i < n; i++)
{
scanf("%d", &c[i]); //每件物品的价值
}
DFS(0, 0, 0); //初始时为第0件物品,当前总重量和总价值均为0
printf("%d\n", maxValue);
return 0;
}
//输入
5 8 //5件物品,背包容量8
3 5 1 2 2 //重量分别为3 5 1 2 2
4 5 2 1 3 //价值
10 //输出结果
上述代码,总是把n件物品的选择全部确定之后才去更新最大价值,但事实上忽视了背包容量不超过V这个特点。
可以只有当sumW<=V时才进入岔道。可以降低计算量(剪枝)
void DFS(int index, int sumW, int sumC)
{
if(index == n)
{
return; //已经完成对n件物品的选择
}
DFS(index + 1, sumW, sumC); //不选第index件物品
//只有加入第index件物品后未超过容量V,才能继续
if(sumW + w[index] <= V)
{
if(sumC + c[index] > ans)
{
ans = sumC + c[index]; //更新最大价值maxValue
}
DFS(index + 1, sumW + w[index], sumC + c[index]); //选第index件物品
}
}