欢迎光临
我们一直在努力

信奥赛C++提高组csp-s之搜索进阶(搜索剪枝案例实践2)

信奥赛C++提高组csp-s之搜索进阶(搜索剪枝案例实践2)

在这里插入图片描述

生日蛋糕 —— 上下界剪枝 + 最优性剪枝

题目描述

7 月 17 日是 Mr.W 的生日,ACM-THU 为此要制作一个体积为

N

π

N\\pi

Nπ

M

M

M 层生日蛋糕,每层都是一个圆柱体。

设从下往上数第

i

i

i

1

i

M

1 \\leq i \\leq M

1iM)层蛋糕是半径为

R

i

R_i

Ri,高度为

H

i

H_i

Hi 的圆柱。当

i

<

M

i \\lt M

i<M 时,要求

R

i

>

R

i

+

1

R_i \\gt R_{i+1}

Ri>Ri+1

H

i

>

H

i

+

1

H_i \\gt H_{i+1}

Hi>Hi+1

由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积

Q

Q

Q 最小。

请编程对给出的

N

N

N

M

M

M,找出蛋糕的制作方案(适当的

R

i

R_i

Ri

H

i

H_i

Hi 的值),使

S

=

Q

π

S=\\dfrac{Q}{\\pi}

S=πQ 最小。

(除

Q

Q

Q 外,以上所有数据皆为正整数)

输入格式

第一行为一个整数

N

N

N

N

2

×

10

4

N \\leq 2 \\times 10^4

N2×104),表示待制作的蛋糕的体积为

N

π

N\\pi

Nπ

第二行为

M

M

M

M

15

M \\leq 15

M15),表示蛋糕的层数为

M

M

M

输出格式

输出一个整数

S

S

S,若无解,输出

0

0

0

输入输出样例 1
输入 1

100
2

输出 1

68

思路分析

问题理解

我们需要制作一个 M 层 的圆柱形蛋糕,从下往上每层的半径

R

i

R_i

Ri和高度

H

i

H_i

Hi 均为正整数,且满足

R

i

>

R

i

+

1

,

 

H

i

>

H

i

+

1

R_i > R_{i+1},\\ H_i > H_{i+1}

Ri>Ri+1, Hi>Hi+1。蛋糕总体积为

N

π

N\\pi

Nπ,要求外表面面积 Q 最小(最下一层的下底面除外)。输出

S

=

Q

/

π

S = Q / \\pi

S=Q/π(整数)。

外表面面积计算方式为:所有层的侧面积之和 + 最底层的下底面积。

解题思路

由于

M

15

M \\le 15

M15

N

20000

N \\le 20000

N20000,直接枚举所有可能的半径和高度组合会爆炸,必须使用DFS + 剪枝。

1. 搜索顺序

从**最底层(第 M 层)开始向最顶层(第 1 层)**搜索。因为底层半径和高度较大,先枚举大值有利于后续剪枝。

2. 状态表示
  • dep:当前正在处理的层数(从 M 到 1)
  • v:已经使用的体积
  • s:已经累积的表面积(注意:当 dep == M 时会加上最底层的下底面积)
3. 预处理最小可能值(剪枝用)
  • minv[i]:从上往下前 i 层的最小体积(假设每层半径 = 高 = i,则体积为

    i

    3

    i^3

    i3

  • mins[i]:前 i 层的最小侧面积(每层侧面积

    2

    π

    r

    h

    2\\pi r h

    2πrh,最小为

    2

    i

    2

    2i^2

    2i2

4. 剪枝策略
  • 体积剪枝:若当前体积 + 剩余层的最小体积 > N,则不可能完成,剪枝。
  • 表面积剪枝:若当前表面积 + 剩余层的最小侧面积 ≥ 当前最优解,则剪枝。
  • 剩余体积的侧面积下界:剩余体积为 N – v,若将其全部做成一个半径为 r[dep+1] 的圆柱(最大可能半径),其侧面积为 2*(N-v)/r[dep+1]。这个值是剩余部分侧面积的下界(半径越大,侧面积越小)。若 s + 下界 ≥ ans,则剪枝。
5. 枚举半径和高度
  • 半径 i: 下限 = dep(保证上面还能放下 dep-1 层) 上限 = min( sqrt(N-v), r[dep+1]-1 )(不能超过下一层半径,且体积限制)
  • 高度 j: 下限 = dep 上限 = min( (N-v)/(i*i), h[dep+1]-1 )(体积限制 + 递减约束)
6. 递归与回溯
  • 更新体积:v + i*i*j
  • 更新表面积:s + 2*i*j,若为最底层(dep == M)还需加上 i*i(下底面积)
  • 递归 dfs(dep-1, new_v, new_s)
7. 输出

若找到解则输出最优的 ans,否则输出 0。


代码实现

#include<bits/stdc++.h>
using namespace std;

const int INF = 0x3f3f3f3f;
int N, M, ans = INF;
int mins[25], minv[25]; // 前 i 层的最小侧面积和最小体积
int r[25], h[25]; // 每层的半径和高度(从下往上存储)

// dep:当前要处理的层数(从 M 向下到 1)
// v:已经使用的体积
// s:已经累积的表面积(最底层下底面积已在 dep==M 时加入)
void dfs(int dep, int v, int s) {
if (dep == 0) { // 所有层处理完毕
if (v == N) ans = min(ans, s); // 体积恰好为 N 时更新答案
return;
}

// 剪枝1:剩余体积加上已有体积仍不足 N,或已超过 N
if (v + minv[dep] > N) return;
// 剪枝2:当前表面积加上剩余层的最小侧面积已经不小于最优解
if (s + mins[dep] >= ans) return;
// 剪枝3:剩余体积对应的最小可能侧面积下界估算
// 剩余体积为 N-v,用当前层的下一层半径(即更大半径)作为半径,
// 得到剩余侧面积的下界(半径越大,侧面积越小)
if (s + 2 * (N v) / r[dep + 1] >= ans) return;

// 枚举当前层的半径,从大到小
// 半径下限:至少 dep(保证上面还能放 dep-1 层)
// 半径上限:不能超过下一层半径-1,且不能超过 sqrt(N-v)(最小高度为1)
for (int i = min((int)sqrt(N v), r[dep + 1] 1); i >= dep; i) {
// 枚举当前层的高度
// 高度下限:至少 dep
// 高度上限:不能超过下一层高度-1,且受体积限制 (N-v)/(i*i)
for (int j = min((N v) / i / i, h[dep + 1] 1); j >= dep; j) {
r[dep] = i;
h[dep] = j;
int cur_s = s + 2 * i * j; // 加上当前层的侧面积
if (dep == M) cur_s += i * i; // 最底层额外加上下底面积
dfs(dep 1, v + i * i * j, cur_s);
}
}
}

int main() {
cin >> N >> M;
// 预处理前 i 层的最小体积和最小侧面积(假设半径 = 高 = i)
for (int i = 1; i <= M; i++) {
minv[i] = minv[i 1] + i * i * i; // 体积:i^3
mins[i] = mins[i 1] + 2 * i * i; // 侧面积:2*i^2
}
// 边界条件:第 M+1 层(虚拟层)半径和高度设为无穷大,以便底层枚举时不受限制
r[M + 1] = h[M + 1] = INF;
dfs(M, 0, 0); // 从最底层开始搜索
cout << (ans == INF ? 0 : ans) << endl;
return 0;
}


功能分析

1. 解决问题

在给定总体积 (N) 和层数 (M) 的情况下,找到使外表面面积最小的蛋糕制作方案,并输出最小的 (S)(整数)。若无解则输出 0。

2. 算法核心
  • 深度优先搜索:枚举每一层的半径和高度,通过递归遍历所有合法组合。
  • 剪枝优化:
    • 可行性剪枝(体积不足或超出)
    • 最优性剪枝(当前面积已不优于已知最优解)
    • 剩余体积的面积下界剪枝
  • 预处理:计算最小可能体积和侧面积,用于剪枝判断。
  • 有序枚举:半径和高度从大到小枚举,优先尝试大值,加速找到较优解并增强剪枝效果。
3. 注意点
  • 表面积的计算方式为 所有层侧面积之和 + 最底层下底面积,这与题目文字描述略有差异,但符合样例和多数 AC 代码的实现。
  • 半径和高度的下限取 dep,保证了递减性且每层至少为 1。

更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》: https://blog.csdn.net/weixin_66461496/category_13113932.html


各种学习资料,助力大家一站式学习和提升!!!

#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}

1、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划: https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL: https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践: https://blog.csdn.net/weixin_66461496/category_13113932.html

2、csp信奥赛冲刺一等奖有效刷题题解:

信奥赛C++普及组csp-j初赛&复赛真题题解(持续更新)https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新) https://blog.csdn.net/weixin_66461496/category_13125089.html

3、GESP C++考级真题题解:

在这里插入图片描述

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

在这里插入图片描述

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

在这里插入图片描述 GESP(C++ 七级+八级)真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_13117178.html

4、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

· 文末祝福 ·

#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}

赞(0)
未经允许不得转载:171主机测评 » 信奥赛C++提高组csp-s之搜索进阶(搜索剪枝案例实践2)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址