带剪枝的深搜

文章介绍了如何使用剪枝技术优化深度优先搜索算法来解决一个格子游戏问题。当n=7时,传统的暴力搜索会面临时间限制,通过剪枝方法可以避免无效路径,例如遇到三面被标记的点作为必经点进行剪枝。代码实现中展示了如何应用这些剪枝策略来计算从左上角到左下角的可行路径数量。
摘要由CSDN通过智能技术生成

what’s 剪枝?

因为深搜都是指数级别的,只要题目数据那么xiao微一大,直接TLE
所以剪枝的重要性就体现出来力。假设你在一棵树上进行深搜,剪枝此时的意思就是字面意思 通过准确且正确的条件进行剪枝,使程序运行时间大大减小

从上到下
时间限制:1秒 内存限制:128M 小可在玩一个格子游戏。格子游戏的地图是一个大正方形,大正方形被分为了 n×n
个小正方形。小可一开始位于左上角的格子,要走到左下角的格子上去。走格子的路径有一个规则,必须路过所有格子恰好一次。如 n=3
时有两种走法:
在这里插入图片描述

现在小可想知道给定 n ,有多少条可行路径
输入描述

一行,一个整数 n

输出描述

一行,一个整数,表示可行路径数

输入样例

3
输出样例

2
数据范围

1<=n<=7

冷静分析:

首先,暴搜看起来异常简单,在一看数据范围似乎确实可以 但细算一下,按上限n=7,此时一共有49个格点,每一个格子有上下左右四种选择——449你猜会不会爆?再多看一眼就会爆炸,毫不犹豫考虑剪枝

剪枝1: 看下面的图 ,走了一圈进了一个死胡同,所以我们的目标就是从死胡同上做文章,but how?
在这里插入图片描述
再看下面的图片,当我们走到中间这个白点时,四周的对点是相反的状态
如果我们往右走,死胡同;往左走,右边的死胡同就进不去了,因为题目规定所有点都得遍历一遍,所以遇到这种情况就剪枝得了

在这里插入图片描述

剪枝2:
在这里插入图片描述

看此图,我们已经走到了点B(然后标记掉),然后点A四周就只有往左的方向没有走过,所以点A只能通过点B走过,所以称点A为点B的必经点
然后如果点B四周出现两个以上的必经点呢?是不是点B只能走其中任意的一个?
那剩下的必经点就变成了三面被标记的死胡同,自然而然就不会产生到达终点的答案
怎么判断一个点是否为必经点?
根据刚才的定义,三面都被标记的点即为必经点,直接计数器++

AC code:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<stack>
#include<map>
#define TIE ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define N 15
#define INF 0x3f3f3f3f
using namespace std;
int n, ans, a[N][N];
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, 1, -1};
int getfree(int x, int y) {
	int cnt = 0;
	for (int i = 0; i < 4; i++) {
		if (a[x + dx[i]][y + dy[i]] == 0)
			cnt++;
	}
	return cnt;
}
void dfs(int x, int y, int step) {
	if (x == n && y == 1) {
		if (step == n * n) {
			ans++;
			return;
		}
	}
	if ((a[x][y - 1] && a[x][y + 1] && a[x - 1][y] == 0 && a[x + 1][y] == 0) ||
	        (a[x][y - 1] == 0 && a[x][y + 1] == 0 && a[x - 1][y] && a[x + 1][y]))
		return;
	int cntbijing = 0;
	int nx, ny;
	for (int i = 0; i < 4; i++) {
		int xx = x + dx[i], yy = y + dy[i];
		if (a[xx][yy] || (xx == n && yy == 1)) continue;
		int cnt = getfree(xx, yy);
		if (cnt == 1) {
			cntbijing++;
			nx = xx, ny = yy;
		}
	}
	if (cntbijing > 1)
		return;
	else {
		if (cntbijing == 1) {
			a[nx][ny] = 1;
			dfs(nx, ny, step + 1);
			a[nx][ny] = 0;
		} else {
			for (int i = 0; i < 4; i++) {
				int xx = x + dx[i], yy = y + dy[i];
				if (a[xx][yy] == 0) {
					a[xx][yy] = 1;
					dfs(xx, yy, step + 1);
					a[xx][yy] = 0;
				}
			}
		}
	}
}
int main() {
	TIE;
	cin >> n;
	for (int i = 0; i <= n + 1; i++) {//设置保护区
		a[0][i] = a[n + 1][i] = a[i][0] = a[i][n + 1] = 1;
	}
	a[1][1] = 1;
	dfs(1, 1, 1);
	cout << ans;
	return 0;
}

完结撒花~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值