[dfs] aw165. 小猫爬山(dfs剪枝与优化+好题)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[dfs] aw165. 小猫爬山(dfs剪枝与优化+好题)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:165. 小猫爬山

相似的搜索顺序:

2. 题目解析

有关搜索顺序:


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剪枝与优化+好题)的主要内容,如果未能解决你的问题,请参考以下文章

DFS剪枝小猫爬山

DFS剪枝小猫爬山

CodeVS 4228 小猫爬山 - DFS

[dfs] aw1118. 分成互质组(dfs搜索顺序+dfs状态定义+最大团+好题)

算法进阶指南(DFS和BFS)--- 小猫爬山

165. 小猫爬山爆搜+剪枝