深搜的剪枝技巧

剪枝方法

1.优化搜索顺序

在不同的问题中,搜索树的各个层次、各个分支之间的顺序不是固定的,不同的搜索顺序会产生不同的搜索树形态,其规模大小也相差甚远。

2.排除等效冗余

在搜索过程中,若能判断从搜索树当前节点上沿某几条不同分支到达的子树是相同的,那么只需对其中一条分支执行搜索。

3.可行性剪枝

可行性剪枝也叫上下界剪枝,其是指在搜索过程中,及时对当前状态进行检查,若发现分支已无法到达递归边界,就执行回溯。

4.最优性剪枝

在最优化问题的搜索过程中,若当前花费的代价已超过当前搜索到的最优解,那么无论采取多么优秀的策略到达递归边界,都不可能更新答案,此时可以停止对当前分支的搜索进行回溯。

题目

p s ps ps:懒得放题解了,凑合看吧,zzz

LOJ #10018. 「一本通 1.3 例 1」数的划分

题目链接

题意:将整数 n n n分成 k k k份,且每份不能为空,问有多少种不同的分法

直接搜会 T T T,要想剪枝:

  1. 每次搜索都从上一个搜到的数开始搜,去重
  2. 剩余未被划分的数不能小于剩余划分的次数

借大佬说的话:

因为保证没有重复方案,就必须保证枚举的结果递增(或者递减)。假设当前选择的数为 x x x,那么后面所有的数必须 ≥ x ≥x x,如果此时让剩下的数都 = x =x =x,还比 n n n小,那么剩下的几次递归其实都是无用的,这就是这一题的可行性剪枝。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

inline int read() {
   
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}

int n, k, ans;

void dfs(int last, int cnt, int sum) {
   
	if(sum > n) return;
	if(cnt == k) {
   
		if(sum == n) ans++;
		return;
	}
	for(int i = last; sum + i * (k - cnt) <= n; i++) {
   
		dfs(i, cnt + 1, sum + i);
	}
}

int main() {
   
	n = read(), k = read();
	dfs(1, 0, 0);
	cout << ans << '\n';
	return 0;
}

LOJ #10019. 「一本通 1.3 例 2」生日蛋糕

题目链接

#include <cmath> 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 30;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
   
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, m;
int minv[A], mins[A], ans = inf, h[A], r[A], s = 0, v = 0;

void dfs(int dep) {
   
	if(!dep) {
   
		if(v == n) ans = min(ans, s);
		return;
	}
	for(r[dep] = min((int)sqrt(n - v), r[dep + 1] - 1); r[dep] >= dep; r[dep]--)
		for(h[dep] = min((int)((double)(n-v) / r[dep] / r[dep]), h[dep + 1] - 1); h[dep] >= dep; h[dep]--) {
   
			if (v + minv[dep - 1] > n) continue;
			if (s + mins[dep - 1] > ans) continue;
			if (s + (double)2 * (n - v) / r[dep] > ans) continue;
			if(dep == m) s += r[dep] * r[dep];
			s += 2 * r[dep] * h[dep];
			v += r[dep] * r[dep] * h[dep];
			dfs(dep - 1);
			if (dep == m) s -= r[dep] * r[dep];
			s -= 2 * r[dep] * h[dep];
			v -= r[dep] * r[dep] * h[dep];
		}
}

int main() {
   
	n = read(), m = read();
	minv[0] = mins[0] = 0;
	for(int i = 1; i <= m; i++) {
   
		minv[i] = minv[i - 1] + i * i * i;
		mins[i] = mins[i - 1] + i * i;
	}
	h[m + 1] = r[m + 1] = inf;
	dfs(m);
	cout << ans << "\n";
	return 0;
}

LOJ #10020. 「一本通 1.3 例 3」小木棍

题目链接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

inline int read() {
   
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	return x * f;
}

const int N = 101;

int n, a[N], sum, len, maxn, vis[N], cnt, ll, tot;


bool dfs(int now, int lo, int last) {
   
	if(now == tot + 1 && lo == len) return true;
	if(lo == len) lo = 0, last = 0;
	for(int i = last + 1; i <= tot; i++) {
   
		if(vis[i]) continue;
		if(a[i] + lo > len) continue;
		vis[i] = 1
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值