[dfs] aw165. 小猫爬山(dfs剪枝与优化+好题)
Posted Ypuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[dfs] aw165. 小猫爬山(dfs剪枝与优化+好题)相关的知识,希望对你有一定的参考价值。
1. 题目来源
链接:165. 小猫爬山
相似的搜索顺序:
2. 题目解析
有关搜索顺序:
- 本题和 [dfs] aw1118. 分成互质组(dfs搜索顺序+dfs状态定义+最大团+好题) 的搜索顺序一致。顺序枚举每个猫,枚举所有组,看当前猫能放到哪个组中即可。 这样一定能枚举到所有情况,记录最小组数即可。
dfs
四大优化:
- 优化1:优化搜索顺序。在有剪枝的存在下,优化搜索顺序很重要。优先搜索分支较少的节点。 在本题中,我们在枚举猫放小车时,按猫的体积从大到小开始枚举,这样体积大的猫放进去使得剩余空间变小,剩余的可选情况变少,分支变少。 一个形象的例子就是,如果这个胖猫体积直接是车的体积,那么后面一系列就不需要枚举了。而这个轻猫体积很轻,后面还有一长串需要枚举的体积和猫。
- 优化2:排除等价冗余。这点主要是针对组合类型枚举使用,例如求
n
个苹果中选 3 个,问选的所有方案。那么这些方案内的苹果顺序是无关的,所以是组合,而不是排列。故按照组合类型枚举,传入下标,保证下标单调性,会大大提高搜索效率!在本题中未使用,因为本题按照猫顺序枚举,已经不会发生重复情况。但在 [dfs] aw1118. 分成互质组(dfs搜索顺序+dfs状态定义+最大团+好题) 中,枚举组或者枚举车的话,就会出现重复情况,需要用组合枚举来加快枚举顺序。 - 优化3:可行性剪枝。在枚举过程中发现已经不满足限制条件时,不可能达到最优解时,直接返回即可,进行剪枝。在本题中及 [dfs] aw1118. 分成互质组(dfs搜索顺序+dfs状态定义+最大团+好题) 均有体现。
- 优化4:最优性剪枝。在枚举过程中发现中间值已经无法更新最优解时,直接返回即可。在本题中及 [dfs] aw1118. 分成互质组(dfs搜索顺序+dfs状态定义+最大团+好题) 均有体现。
至于 dfs
记忆化搜索,那属于 dp
,在此不讨论。
体会这四大优化,好好写暴力!
时间复杂度: O ( 指 数 级 ) O(指数级) O(指数级)
空间复杂度: O ( n ) O(n) O(n)
写法 1,写法简洁,推荐。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 25;
int n, m;
int w[N];
vector<int> g[N];
int res = 1e9, len = 0;
void dfs(int u) {
// 最优性剪枝
if (len >= res) return ;
// 搜到答案
if (u == n) {
res = min(res, len);
return ;
}
for (int i = 0; i < len; i ++ ) {
int t = 0;
for (int j = 0; j < g[i].size(); j ++ ) t += g[i][j];
// 可行性剪枝
if (m - t >= w[u]) {
g[i].push_back(w[u]);
dfs(u + 1);
g[i].pop_back();
}
}
g[len ++ ].push_back(w[u]);
dfs(u + 1);
g[ -- len].pop_back();
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ ) scanf("%d", w + i);
// 优化搜索顺序
sort(w, w + n, greater<int>());
dfs(0);
printf("%d\\n", res);
return 0;
}
写法 2,空间更少,更加容易理解。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 25;
int n, m;
int w[N];
int sum[N];
int res = 1e9;
// 枚举到第 u 个数,共 k 组
void dfs(int u, int k) {
// 最优性剪枝
if (k >= res) return ;
// 搜到答案
if (u == n) {
res = min(res, k);
return ;
}
for (int i = 0; i < k; i ++ ) {
// 可行性剪枝
if (sum[i] + w[u] <= m) {
sum[i] += w[u];
dfs(u + 1, k);
sum[i] -= w[u];
}
}
// k 为下标就是新的一组
sum[k] = w[u];
dfs(u + 1, k + 1);
sum[k] = 0; // 恢复现场
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i ++ ) scanf("%d", w + i);
// 优化搜索顺序
sort(w, w + n, greater<int>());
dfs(0, 0);
printf("%d\\n", res);
return 0;
}
以上是关于[dfs] aw165. 小猫爬山(dfs剪枝与优化+好题)的主要内容,如果未能解决你的问题,请参考以下文章