Luogu 1731 生日蛋糕

题目链接:https://www.luogu.org/problem/P1731

思路:

一道非常棒的搜索剪枝题。

考虑从底向上进行搜索,在一个区间内枚举$r,h$。

考虑优化:

1.区间范围的优化

体积的公式是由$r,h$共同确定的,记录之前每一层的$r$,从之前的$r-1$开始枚举$r$。

对于$h$,根据公式,设剩余体积为$v_r$,可以把$min(v_r/(r *r),hlast-1)$作为上界。

2.搜索顺序优化

使用倒序搜索或许可以对搜索时间起到优化。

3.可行性、最优性剪枝

不难发现,当$1,2,3,...m$层的$h,r$皆为$1,2,3,...m$时,所得到的$v,s$最小。

可以选择预处理出前$i$层的最小$v,s$,与目前已经搜到的结果、最大体积$n$进行比较,进行剪枝。

还有一个比较难想的剪枝,在这里不想打$Latex$了,引用一位网上dalao的博客吧

代码:

#include <bits/stdc++.h>
const int INF = 1 << 30;
const int MAXM = 50;
using namespace std;
int n, m, ans = INF, r[MAXM], h[MAXM], vmin[MAXM], smin[MAXM];
void dfs(int dep, int vnow, int s1, int s2, int s3) {
    if(s3 < 0 || dep < 0)
        return ;
    if(s2 > ans)
        return ;
    if(dep == 0) {
        if(s3 == 0)
            ans = min(ans, s1 + s2);
        return ;
    }
    if(s1 + s2 + smin[dep] > ans)
        return ;
    if(vnow + vmin[dep] > n)
        return ;
    for(int i = r[dep + 1] - 1; i >= dep; i--) {
        if(dep == m)
            s1 = i * i;
        int jj = min(s3 / (i * i), h[dep + 1] - 1);
        for(int j = jj; j >= dep; j--) {
            if(s1 + s2 + ((n - vnow) * 2 / i) > ans)
                continue;
            r[dep] = i;
            h[dep] = j;
            dfs(dep - 1, vnow + i * i * j, s1, s2 + 2 * i * j, s3 - (i * i * j));
            r[dep] = 0;
            h[dep] = 0;
        }
    }
}
int main() {
    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        vmin[i] = vmin[i - 1] + i * i * i;
        smin[i] = smin[i - 1] + 2 * i * i;
    }
    r[m + 1] = h[m + 1] = sqrt(n);
    dfs(m, 0, 0, 0, n);
    ans == INF ? (cout << "0" << endl) : (cout << ans << endl);
    return 0;
}

引用链接:https://www.cnblogs.com/Parsnip/p/10610751.html

原文地址:https://www.cnblogs.com/BeyondLimits/p/11297453.html