dfs的优化详细课(保姆级详细教程,保证让你以后的dfs多获得很多分)

众所周知,dfs是一个非常通俗易懂的算法,并且非常的简单,好学。不过dfs这个算法存在着一些缺点,例如说会超时,今天,教大家几个卡常技巧,可以让你在考试是写的dfs多拿一点分。

我们先来看一个简单的问题,如下:

有n种物质,第i种物质是有a[i]个a元素和b[i]个b元素,并且卖出c[i]元,我需要一个比例恰好为a1:b1的元素,而且我希望花的钱尽可能少。

这是数据范围:

n<=40

a和b的值<10,c的值<100,a1和b1<=10,而且所有数字都是整数。

好了,很多同学看到这个数据范围(n<=40)就慌了,因为我暴力枚举每一个选不选是2^40的时间复杂度,一般来讲,你跑到2^27以上就危险了,所以说我们要想办法优化一下。

这个东西,我们用的方法叫做最值剪枝(我自己发明的一个名字),我们先看一下暴力的正常做法:

#include <bits/stdc++.h>
using namespace std;
const int N = 50;
int n,m,k;
struct owl{
	int a,b,c;
}x[N];
int res = 0x3f3f3f3f;
void dfs(int u,int owl1,int owl2,int s){
	if (u == n){
		if (owl1 * k == owl2 * m && owl1 != 0 && owl2 != 0){
			res = min(res,s);
		}
		return ;
	}
	if (owl1 * k == owl2 * m && owl1 != 0 && owl2 != 0){
		res = min(res,s);
		return ;
	}
	dfs(u + 1,owl1,owl2,s);
	dfs(u + 1,owl1 + x[u].a,owl2 + x[u].b,s + x[u].c);
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin >> n >> m >> k;
	for (int i = 0; i < n; i ++ ){
		cin >> x[i].a >> x[i].b >> x[i].c;
	}
	dfs(0,0,0,0);
	if (res == 0x3f3f3f3f){
		cout << -1;
		return 0;
	}
	cout << res;
	return 0;
}

对吧,是不是这样的?最值剪枝的意思是如果我当前的结果已经超过最小值(res)了,我就没必要继续搜索下去了,我们思考一下为什么?

证明:因为每个数都是正数,所以说你越搜索只会越大,到时候更行了还是当前的ren更小,所以说没有必要继续搜索下去,如下:

还有,这里面还用到了一个东西,叫做当前最优解定理,就是如果我当前已经满足条件了,也没必要搜索下去,因为我肯定少搜索一点我的结果更少。

然后那个最值剪枝只需要加上这一句:

	if (s >= res){
		return ;
	}

就完成了。

还有一种剪枝,叫做性质剪枝,大概如下:

如果对于i来说,已经成为了死路一条的话(不管你怎么搜索,都不行了),那么我们就不搜索,这也是一个降低复杂度的很好的方法。

还有一种,非常重要,叫做记忆化搜索,主要是就相当于把该状态的值记录一下,最后继续搜索的时候直接用这个值就可以了,这样可以直接降低大量时间复杂度。

不过其实每一种方法都有优点和缺点,大家需要根据题目来。

这里附上一些记忆化的详细讲解:dp入门课(五)-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值