深度优先搜索

深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法。

从起点开始前进,当碰到岔道口时,总是选择其中一条岔路前进,在岔路上如果又遇到新的岔道口,

仍然选择新岔道口的其中一条岔路前进,直到碰到死胡同才回退到最近的岔道口选择另一条岔路。

(当碰到岔道口时,总是以“深度”作为前进的关键词,不碰到死胡同就不回头)


例1:

有n件物品,每件物品重量为w[i],价值为v[i]。现在需要选出若干物品放入一个容量为bag的背包中,

使得在选入背包的物品总重量不超过容量bag的情况下,让背包中物品的价值之和最大。

求最大价值。(1<=n<=20)

输入:5 8

          3 5 1 2 2

          4 5 2 1 3

输出:10

解:

public class Main {

	static int n;// 物品数量
	static int bag;// 背包容量
	static int[] weight;// 物品重量
	static int[] value;// 物品价值
	static int max = 0;// 背包能装下的最大价值

	static void dfs(int i, int w, int v) {// 物品序号,当前重量,当前价值
		if (i == n)
			return;
		dfs(i + 1, w, v);// 不选第i件物品
		// 只有在 当前总量 + 第i件物品 不超过背包容量时,才会选下第i件物品
		if (w + weight[i] <= bag) {
			if (v + value[i] > max) {
				max = v + value[i];// 更新最大价值max
			}
			dfs(i + 1, w + weight[i], v + value[i]);// 选第i件物品
		}
	}

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		bag = in.nextInt();
		weight = new int[n];
		value = new int[n];
		for (int i = 0; i < n; i++)
			weight[i] = in.nextInt();
		for (int i = 0; i < n; i++)
			value[i] = in.nextInt();
		dfs(0, 0, 0);// 初始时均为0
		System.out.println(max);
	}
}


例2:

给定n个整数(可能有负数),从中选择k个数,使得这k个数之和恰好等于一个给定的整数x。

如果有多种方案,选择它们中平方和最大的一个。

数据保证这样的方案唯一。

输入:4
          2 3 3 4
          2  

          6

输出:2 4

解:

public class Main {

	static int n;
	static int k;// 从a中选k个数
	static int x;// 使这k个数之和为x
	static int[] a;
	static Integer[] b;// 最优方案
	static LinkedList<Integer> c;// 临时方案
	static int maxSumP = 0;// 最大平方和

	// 整数编号,当前已经选择的个数,当前已选整数之和,当前已选整数的平方和
	static void dfs(int i, int nowK, int sum, int sumP) {
		if (nowK == k && sum == x) {
			if (sumP > maxSumP) {// 当sumP为最大时,此时的方案作为最优方案
				maxSumP = sumP;
				b = c.toArray(b);
			}
			return;
		}
		// 已经遍历完n个数,或选了超过k个数,或和超过x,均返回
		if (i == n || nowK > k || sum > x)
			return;
		c.add(a[i]);// 编号为i的数进入临时队列
		dfs(i + 1, nowK + 1, sum + a[i], sumP + a[i] * a[i]);// 选编号为i的数
		c.pollLast();// 编号为i的数退出临时队列
		dfs(i + 1, nowK, sum, sumP);// 不选编号为i的数
	}

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		a = new int[n];
		for (int i = 0; i < n; i++)
			a[i] = in.nextInt();
		k = in.nextInt();
		b = new Integer[k];
		c = new LinkedList<>();
		x = in.nextInt();
		dfs(0, 0, 0, 0);// 初始均为0
		for (int i = 0; i < k; i++)
			System.out.print(b[i] + " ");
	}
}

上面问题中的每个数都只能选择一次,现在稍微修改题目:

假设n个整数中的每一个都可以都可以被选择多次,那么选择k个数,使得k个数之和恰好为x。

假设有三个数1、4、7,需要从中选5个数,使它们的和为17。

显然只需要选3个1和2个7,即可得到17。

解:

由于每个整数都可以被选择多次,因此当选择了i号数时,不应当直接进入i+1号数的处理。

应当继续选择i号数,直到某个时刻决定不再选择i号数,就会通过“不选编号为i的数”这条分支进入i+1号数的处理。

因此只需要把“选编号为i的数”这条分支的代码中的i+1修改为i即可。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值