关于dfs的剪枝

深度优先搜索是一种搜索策略,但是他是通过递归(或循环)来实现的,那么时间复杂度便不会低。通过dfs,我们可以得到一颗搜索树,但实际上这棵树的许多枝条是不需要的,那么我们没必要对这个分支进行搜索,砍掉这个子树,便是剪枝。

1.可行性剪枝

   如果当前条件不合法就不再继续搜索,直接return。这是非常好理解的剪枝。

    问题:给定n个数,要求选出k个数,使得选出的数和为sum。

代码如下:

import java.util.Scanner;

public class L1 {

	static int n, k, sum, ans = 0;
	static int[] a = new int[1005];

	static void dfs(int i, int cnt, int s) {
		if (cnt > k)// 限制条件1,选的个数超过k
			return;
		if (s > sum)// 限制条件2,和大于sum
			return;
		if (i == n) {
			if (cnt == k && s == sum) {
				ans++;
			}
			return;
		}
		dfs(i + 1, cnt, s);
		dfs(i + 1, cnt + 1, s + a[i]);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		k = sc.nextInt();
		sum = sc.nextInt();
		for (int i = 0; i < n; i++)
			a[i] = sc.nextInt();

		dfs(0, 0, 0);
		System.out.println(ans);
	}

}

2.最优性剪枝

如果当前条件所创造出的答案必定比之前的答案大,那么剩下的搜索就毫无必要,甚至可以剪掉。我们利用某个函数估计出此时条件下答案的‘下界’,将它与已经推出的答案相比,如果不比当前答案小,就可以剪掉。尤其是求解迷宫最优解时。

题目:有一个n x m大小的迷宫,字符 'S'表示起点,字符'T'表示终点, '*'表示墙壁,字符'.'表示平地。求S到T的最少步数。

代码如下:

import java.util.Scanner;

public class L9 {

	static int n, m, ans = Integer.MAX_VALUE;
	static char[][] map = new char[110][110];
	static boolean[][] vis = new boolean[110][110];
	static int[][] dir = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };

	static boolean in(int x, int y) {
		return 0 <= x && x < n && 0 <= y && y < m;
	}

	static void dfs(int x, int y, int step) {
		if (step >= ans) {// 如果步数已经超过当前最优解,那么直接剪掉
			return;
		}
		if (map[x][y] == 'T') {
			ans = step;
			return;
		}
		vis[x][y] = true;
		for (int i = 0; i < 4; i++) {
			int tx = x + dir[i][0];
			int ty = y + dir[i][1];
			if (in(tx, ty) && !vis[tx][ty] && map[tx][ty] != '*') {
				dfs(tx, ty, step + 1);
			}
		}
		vis[x][y] = false;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		for (int i = 0; i < n; i++) {
			map[i] = sc.next().toCharArray();
		}
		int x=0,y=0;
		for(int i=0;i<n;i++) {
			for(int j=0;j<m;j++) {
				if(map[i][j]=='S') {
					x=i;
					y=j;
				}
			}
		}
		dfs(x,y,0);
		System.out.println(ans);
	}

}

3.重复性剪枝

对于某一些特定的搜索方式,一个方案会被搜索很多次,这样是没有必要的。

对于第一题,通过每次选取一个数,下次再选另外的数的思路,可以通过规定选取数的次序使用重复性剪枝。

代码如下:

import java.util.Scanner;

public class L2 {

	static int n, k, sum, ans;
	static int[] a = new int[40];
	static boolean[] xuan = new boolean[40];

	static void dfs(int s, int cnt, int pos) {
		if (s > sum || cnt > k)
			return;
		if (s == sum && cnt == k) {
			ans++;
			return;
		}
		for (int i = pos; i < n; i++) {
			if (!xuan[i]) {
				xuan[i] = true;
				dfs(s + a[i], cnt + 1, i + 1);
				xuan[i] = false;
			}
		}

	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		k = sc.nextInt();
		sum = sc.nextInt();
		for (int i = 0; i < n; i++)
			a[i] = sc.nextInt();
		dfs(0, 0, 0);
		System.out.println(ans);
	}

}

4.奇偶性剪枝

先看题目:有一个n x m 的迷宫,其中字符 ‘S’代表起点,字符‘D’代表终点,字符‘X’代表墙壁,字符‘.’代表平地,从S到D,每次行动消耗1时间,走过的路都会坍塌,因此不能原地不动或回头,现在一直大门会在T时间打开,判断在0时间能否逃离迷宫。

我们只需要dfs每条路径,只搜到T时间就可以了,但是还可以继续剪枝。

代码如下:

import java.util.Scanner;

public class L10 {

	static int n, m, T;
	static char[][] map = new char[10][10];
	static boolean[][] vis = new boolean[10][10];
	static int[] dx = { 0, 0, -1, 1 };
	static int[] dy = { 1, -1, 0, 0 };
	static boolean ok;

	static void dfs(int x, int y, int t) {
		if (ok)
			return;
		if (t == T) {
			if (map[x][y] == 'D')
				ok = true;
			return;
		}
		vis[x][y] = true;
		for (int i = 0; i < 4; i++) {
			int tx = x + dx[i];
			int ty = y + dy[i];
			if (tx < 0 || tx >= n || ty < 0 || ty >= m || map[tx][ty] == 'X' || vis[tx][ty])
				continue;
			dfs(tx, ty, t + 1);
		}
		vis[x][y] = false;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		T = sc.nextInt();
		for (int i = 0; i < n; i++) {
			map[i] = sc.next().toCharArray();
		}
		int sx = 0, sy = 0, ex = 0, ey = 0;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (map[i][j] == 'S') {
					sx = i;
					sy = j;
				}
				if (map[i][j] == 'D') {
					ex = i;
					ey = j;
				}
			}
			if ((sx + sy + ex + ey + T) % 2 != 1) {// 奇偶性剪枝
				System.out.println("NO");
			} else {
				ok = false;
				dfs(sx, sy, 0);
				if (ok) {
					System.out.println("Yes");
				} else {
					System.out.println("No");
				}
			}
		}
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DFS(深度优先搜索)是一种常见的图遍历算法,它使用递归或栈的方式,从一个顶点出发,沿着一条路径一直到达最深的节点,然后回溯到上一层继续遍历其他节点。DFS常被用于解决图的连通性问题、路径问题等。在实际应用中,可以使用DFS进行状态搜索、图的遍历、拓扑排序等。 剪枝是指在搜索过程中,通过一系列的策略判断,提前终止当前搜索分支,并跳过一些无用的搜索路径,从而减少搜索时间。剪枝的核心在于提前排除某些明显不符合条件的状态,以减少无效搜索的时间开销,提高效率。在算法设计中,剪枝通常会利用一些特定的性质或条件进行判断,从而缩小搜索空间。 动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划通常用于求解最优化问题,它通过定义状态和状态转移方程,采用自底向上的思路,逐步求解每个子问题的最优值,最终得到原问题的最优解。动态规划的核心是存储已经计算过的子问题的解,避免了重复计算。 贪心算法是一种基于局部最优解的策略,它通过每一步选择在当前状态下最优的解,以期望得到全局最优解。贪心算法的基本思想是由局部最优解推导出全局最优解,通常通过贪心选择性质、最优子结构和贪心选择构成三部分。贪心算法相比其他算法,如动态规划,它的优势在于简单、高效,但缺点在于不能保证获取到全局最优解,只能得到一个近似解。 综上所述,DFS剪枝、动态规划和贪心算法在算法设计和问题求解中都发挥着重要的作用。具体使用哪种算法取决于问题的性质和要求,需要在实际应用中进行综合考虑和选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值