《挑战程序设计竞赛》中的一道题:
给定整数 a 1 、a 2 、…、a n ,判断是否可以从中选出若干数,使它们的和恰好为 k。
输入
n=4
a={1,2,4,7}
k=13
输出
Yes (13 = 2 + 4 + 7)
/***
* 深度优先算法(Depth-First-Search)
* 从数组a中选出几个数, 使它们的和恰好为k
* 本题中有n=4个数字, 选择其中的几个使它们的和为k=13
***/
#include <iostream>
using namespace std;
int a[4] = {1,2,4,15}; //将待选择的数字存到数组中
int n = 4, k = 13;
//已经从前i项得到了和sum, 然后对i项之后的进行分支
bool dfs(int i, int sum) {
//如果前n项都计算过了, 则返回sum是否与k相等
if (i == n) return sum == k; //所有的遍历都必须到达n层以后才会返回true或false
//不加上a[i]的情况
if (dfs(i + 1, sum)) return true;
//加上a[i]的情况
if (dfs(i + 1, sum + a[i])) return true;
//即, 在更深一层的i+1层, 不论加a[i]还是不加a[i], 只要其中有一个情况能返回true, 则i这一层就返回true
//最后层层向上传递, 直到第0层返回true.
//无论是否加上a[i]都不能凑成k就返回false
return false;
}
int main(){
if (dfs(0, 0))
printf("Yes\n");
else
printf("No\n");
}
这里用更简单的例子来推导具体的实现过程,
a[2] = {13, 1};
k = 13 , n =2;
第一层:从dfs(0, 0)开始, i = 0, sum = 0; 因为i不等于 2,则进行是否包含a[0]的判断, 不包含, 则调用dfs(1, 0), 包含则调用dfs(1, 13),
第二层:花分两朵,先看左边这支:dfs(1, 0),i不等于2,则进行判断是否包含a[1],不包含则调用dfs(2, 0),包含则调用dfs(2, 1),
第三层:继续看dfs(2, 0)。此时i等于2,则比较sum和k,显然不相等, 返回false, 继续看dfs(2,1), 也是返回false, 因此,返回到第二层dif(1, 0),其返回false,
接着看第二层看另一支花:dfs(1, 13),i不等于2,则进行判断是否包含a[1],不包含则调用dfs(2,13),包含则调用dfs(2, 14),
第三层:继续看dfs(2,13), i等于2, 则比较sum和k,相等,返回true,这里即不必调用dfs(2, 14)了, 直接返回到第二层dfs(1, 13),
回到第一层,综合这两朵花dfs(1,0) 和dfs(1, 13),dfs(1,0)是false, 被忽略,dfs(1, 13)是true, 被return true,所以dfs(0, 0) 返回true.
综合来看,dfs(i, sum)除非到达最后一层,否则就会先看 第i+1层,只要包含或者不包含a[i]这两种情况之一是true, 则dfs(i, sum)就返回true, 是false的那一支将被忽略, 如dfs(0, 0)的真假取决于dfs(1, 0) 和dfs(1, 13)的真假, 因为dfs(1, 13)为真, 所以dfs(0, 0)为真.