深度优先搜索之部分和(平行状态下的求解)

1. 问题描述:

 给定整数序列a1,a2,...,an,判断是否可以从中选出若干数,使它们的和恰好为k.
1≤n≤20
-10^8≤ai≤10^8
-10^8≤k≤10^8
样例:
输入:

n=4
a={1,2,4,7}
k=13
输出:
Yes (13 = 2 + 4 + 7)

2. 思路分析:

一看到题目我们直觉的思维是使用暴力破解来进行求解,虽然可以求解出来,但是涉及到未知的多个数字那么求解花费的时间是非常长的

除了暴力破解我们通常使用的思路是使用dfs来求解,虽然说dfs在某些情况下不是最优的,但是有的时候往往是最有效的

因为dfs可以帮助我们无死角的搜索出所有可能的解,我们经过筛选之后那么就可以得到要求解的目标值

对于这个问题而言,我们可以这样分析,我们对于给出的若干个数字来拼接给出的目标数字,可以选择要当前这个元素,也可以不要当前这个元素,那么上面两种选择形成了第一层,对于上面形成的这一层我们可以继续这样做,可以要当前这个元素,也可以不要当前这个元素,那么经过若干次的操作之后这就形成了一棵完全的二叉树,我们就可以使用dfs来解决了,因为每一个元素它都是同样处理的,分别是要这个当前的元素和不要这个元素,这也就形成了两个平行的状态,左右子树分别递归来处理

因为在选择的时候凑的数字在变小,那么对应的状态也在改变,那么在递归的方法中我们需要传递的参数是需要凑的剩下的数值,记录下当前第几层的变量,而且为了方便还需要把可选择的数字作为数组传递进去

有了思路之后对于递归和dfs而言:我们最重要的考虑方法中参数的传递和参数的变化,这也就对应着状态的转移,从一个状态转移到下一个状态

对于这道题目烦人的不是判断是否可以凑成这个数字,而是要记录下拼凑过程中拼凑的数字,那么这就需要对dfs调用和退出调用的时候的状态是怎样的比较熟悉才可以更好的理解,因为dfs是一条路走到黑,碰到return之后它会返回到上一层的兄弟继续递归然后再走下去,对于这道题目而言,可以明显看出存在着两个平行的状态,所以在递归调用完成之后退回来的时候就需要考虑碰到return返回到这一层的时候是什么情况,我的思路是对于左边的数字我们是选择当前的元素,对于右边的元素是不选择当前的元素,那么这就要想清楚了:

当左边的这一层退回来的时候那么此时说明这一层加入的元素是不能够组成凑成目标元素的,所以这就要在调用完返回到这一层的时候把加入的上一个元素给移除掉,在继续执行平行状态的右子树的搜索,而加入当前元素是在递归一开始的时候就加入的

3. 具体的代码如下:

import java.util.ArrayList;
import java.util.Scanner;
public class Main{
	static int kk;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int arr[] = new int[n];
		for(int i = 0; i < n; i++){
			arr[i] = sc.nextInt();
		}
		int k = sc.nextInt();
		kk = k;
		dfs(arr, k, 0, new ArrayList<Integer>());
		sc.close();
	}
	
	private static void dfs(int[] arr, int k, int cur, ArrayList<Integer> ints) {
		if(k == 0){
			System.out.print("Yes (" + kk + " = ");
		    int size = ints.size();
		    for(int i = 0; i < size; i++) {
		        System.out.print(ints.get(i) + (i == size - 1 ? "" : " + "));
		    }
		    System.out.println(")");
		    System.exit(0);
		}
		if(k < 0 || cur == arr.length) return;	
		dfs(arr, k, cur + 1, ints);
		ints.add(arr[cur]);
		dfs(arr, k - arr[cur], cur + 1, ints);
		ints.remove(ints.size() - 1);
	}
}

 

总结一下:关键是要这样想:要选这个元素还是不选这个元素,每一层元素都是这样那么最后就形成了对所有元素都可以使用同样方法处理的完全二叉树,此外需要考虑在平行状态下左子树退回来的时候此时是什么状态需要做怎样的处理,右子树退回来需要怎么样处理,而且在递归的哪一个地方加入和需要考虑参数的变化,最后便是对于出口的处理,这里可能涉及到越界的问题,那么需要结合具体的例子来对边界进行一些细节的处理

理解好了两个平行状态后退回去状态的处理那么对于其他的类似的问题是要dfs来处理也比较简单了

下面以一个简单的例子来说明一下:

输入:

6
1 4 7 2 3 5 
6

把具体的例子结合代码就更好理解了,调用完某一层之后会层层返回层层更新,最后得到我们需要的答案

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值