DFS之剪枝与优化(二)

简介: AcWing 167. 木棒

AcWing 167. 木棒

本题链接:AcWing 167. 木棒

本博客提供本题截图:

image.png

本题解析

所有木棍的长度之和为sum, 我们的木棒长度为length,注意我们只有sum % length == 0的时候才会进行dfs这个操作

优化搜索顺序:按照木棍的质量从大到小去枚举木棍

排除等效冗余:

1.按照组合数的方式进行枚举

2.如果当前木棍加入到当前木棒中失败了,则跳过所有长度和当前木棍相等的木棍

3.如果加入木棒的第一根木棍就失败了,则一定失败

4.如果加入木棒的最后一根木棍失败了,则一定失败


我们的dfs中传入三个参数:第u组,第u组的长度,第u组开始枚举的位置


AC代码

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 70;
int n;
int w[N];
int sum, length;
bool st[N];
bool dfs(int u, int cur, int start)
{
    if (u * length == sum) return true;
    if (cur == length) return dfs(u + 1, 0, 0);
    for (int i = start; i < n; i ++ )
    {
        if (st[i] || cur + w[i] > length) continue;
        st[i] = true;
        if (dfs(u, cur + w[i], i + 1)) return true;
        st[i] = false;
        if (!cur || cur + w[i] == length) return false;
        int j = i;
        while (j < n && w[j] == w[i]) j ++ ;
        i = j - 1;
    }
    return false;
}
int main()
{
    while (cin >> n, n)
    {
        memset(st, 0, sizeof st);
        sum = 0;
        for (int i = 0; i < n; i ++ )
        {
            cin >> w[i];
            sum += w[i];
        }
        sort(w, w + n);
        reverse(w, w + n);
        length = 1;
        while (true)
        {
            if (sum % length == 0 && dfs(0, 0, 0))
            {
                cout << length << endl;
                break;
            }
            length ++ ;
        }
    }
    return 0;
}

AcWing 168. 生日蛋糕

本题链接:AcWing 168. 生日蛋糕

本博客提供本题截图:

image.png

本题解析

玄学剪枝题(逃

本题时间卡的特别死,且推导过程难得离谱

来说下剪枝:

1.先枚举半径再枚举高度,原因:因为在计算体积的时候半径是二次方,对结果的影响较大

2.第u层到最高层的最小面积和体积时固定的,即我们的minv数组函数和mins数组

3.第u层的半径满足:u ≤ R(u) ≤ min{R(u + 1) - 1, sqrt(n - v)} (n是总体积,v是当前的体积)

  第u层的高度满足:u ≤ H(u) ≤ min{H(u + 1) - 1,(n - v) / r * r}


推导:我们知道体积公式:V = r * r * h,所以当我们的h取1的时候,有V ≥ r * r,即n - v ≥ r * r,的r满足:r ≤ sqrt(n - v),高度的推导类似于次,但是因为已经确定r了,所以不能把r取任何值


4.玄学剪枝:利用表面积和体积之间的关系进行剪枝(离谱,玄幻)

当我们的当前面积S + 未来表面积S1~u如果≥ ans的话我们就不需要这个分支了

image.png

AC代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 25, INF = 1e9;
int n, m;
int minv[N], mins[N];
int R[N], H[N];
int ans = INF;
void dfs(int u, int v, int s)
{
    if (v + minv[u] > n) return;
    if (s + mins[u] >= ans) return;
    if (s + 2 * (n - v) / R[u + 1] >= ans) return;
    if (!u)
    {
        if (v == n) ans = s;
        return;
    }
    for (int r = min(R[u + 1] - 1, (int)sqrt(n - v)); r >= u; r -- )
        for (int h = min(H[u + 1] - 1, (n - v) / r / r); h >= u; h -- )
        {
            int t = 0;
            if (u == m) t = r * r;
            R[u] = r, H[u] = h;
            dfs(u - 1, v + r * r * h, s + 2 * r * h + t);
        }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i ++ )
    {
        minv[i] = minv[i - 1] + i * i * i;
        mins[i] = mins[i - 1] + 2 * i * i;
    }
    R[m + 1] = H[m + 1] = INF;
    dfs(m, 0, 0);
    if (ans == INF) ans = 0;
    cout << ans << endl;
    return 0;
}

三、时间复杂度

dfs时间复杂度为指数级别


目录
相关文章
|
1月前
生日蛋糕(dfs剪枝)
生日蛋糕(dfs剪枝)
21 0
|
7月前
|
C++
DFS之剪枝与优化
DFS之剪枝与优化
47 0
|
6天前
|
算法
DFS算法及应用(一)
DFS(深度优先搜索)是一种图遍历算法,常用于解决穷举问题,如全排列、迷宫问题、图的连通性等。它沿着树的深度分支进行探索,直至达到叶子节点,若无法继续则回溯。例如,将数字6拆分为3个正整数的递增序列问题可以通过DFS实现,类似地,分糖果问题和买瓜问题同样可以用DFS求解。DFS通常涉及递归或栈结构,通过标记已访问节点避免重复。在编程中,会定义递归函数,设定结束条件,然后枚举可能的情况,并处理下一层节点。
|
6天前
|
算法 索引
DFS算法及应用(二)
回溯:回溯就是DFS的一种,在搜索探索过程中寻找问题的解,当发现不满足求解条件时,就回溯返回,尝试其他路径。
|
1月前
|
存储 算法
算法系列--递归,回溯,剪枝的综合应用(3)(上)
算法系列--递归,回溯,剪枝的综合应用(3)(上)
28 0
算法系列--递归,回溯,剪枝的综合应用(3)(上)
|
1月前
|
算法
算法系列--递归,回溯,剪枝的综合应用(1)(上)
算法系列--递归,回溯,剪枝的综合应用(1)
29 0
|
1月前
|
算法
算法系列--递归,回溯,剪枝的综合应用(2)(上)
算法系列--递归,回溯,剪枝的综合应用(2)
34 0
|
1月前
|
算法 Java
算法系列--递归,回溯,剪枝的综合应用(2)(下)
算法系列--递归,回溯,剪枝的综合应用(2)(下)
42 0
|
1月前
|
算法
算法系列--递归,回溯,剪枝的综合应用(3)(下)
算法系列--递归,回溯,剪枝的综合应用(3)(下)
22 0
|
1月前
|
机器学习/深度学习 消息中间件 算法
DFS - 常见算法题总结
DFS - 常见算法题总结