AcWing 168. 生日蛋糕
DFS剪枝经典题,前三个剪枝方法很经典,最后一个推公式很难想,有空可以多看看
#include<bits/stdc++.h>
using namespace std;
const int N = 25, INF = 1e9;
int n, m;
int R[N], H[N];
int res = INF;
int minv[N], mins[N];
void dfs(int u, int v, int s){ //第u层,体积为v,面积为s
//剪枝,当前积累的体积/面积加上这一层的最小体积/面积不应该大于最终体积/面积
if(minv[u] + v > n) return ;
if(mins[u] + s >= res) return ;
//剪枝,公式推导,最难想的一步,用放缩法
if(s + 2 * (n - v) / R[u + 1] >= res) return ;
if(!u){ //如果搜到了最后一层
if(v == n) res = s;
return ;
}
for(int r = min(R[u + 1] - 1, (int)sqrt((n - v - minv[u - 1]) / u)); r >= u; r -- ){
for(int h = min(H[u + 1] - 1, (n - v - minv[u - 1]) / r / r); h >= u; h -- ){
H[u] = h;
R[u] = r;
int t = 0;
if(u == m) t = r * r; //遍历到最后一层就需要加上蛋糕的上表面积
dfs(u - 1, v + r * r * h, s + h * 2 * r + t); //继续遍历上一层
}
}
}
int main()
{
cin>>n>>m;
//初始化,剪枝操作之一,在保证最上层的高度半径为1的情况下,初始化每层的最小面积和体积
for(int i = 1; i <= m; i ++ ){
minv[i] = minv[i - 1] + i * i * i; //每层的最小体积
mins[i] = mins[i - 1] + 2 * i * i; //每层的最小面积
}
H[m + 1] = R[m + 1] = INF; //哨兵位,防止无限搜下去
dfs(m, 0, 0); //从最下面体积面积最大的层开始搜,减少搜索数量,剪枝操作之一
if(res == INF) res = 0;
cout<<res<<endl;
return 0;
}