生日蛋糕 POJ - 1190 (DFS)

一道稍有难度的Dfs题,首先仔细寻找以下搜索节点的状态,也就是求在蛋糕有n层,底层最大半径为r,最高高度为h的情况下能凑出来的最大体积

接着要考虑一下搜索顺序,搜索范围和剪枝问题。顺序就好比玩七巧板,肯定是先把大的给定下来,也就是最低往上先大后小。

搜索范围最大不过只有两层,上层全部是1,其余全部放在底层。

剪枝的话这题中有很多可以剪枝的地方,因为数据量不是太大我只剪了几个,代码里已有注释写出~

//Poj1190 生日蛋糕
#include <iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;

int N, M, minArea = 1<<30;
int area = 0;  //成品蛋糕的表面积
int minV[25], minA[25];//n层蛋糕最小体积,侧面积
//搜索满足共有n层,体积为v,第n层半径r,高度h的状态
int MaxVforNRH(int n, int r, int h)
{ //求在蛋糕有n层,底层最大半径为r,最高高度为h的情况下能凑出来的最大体积
    int v = 0;
    for(int i = 0; i < n; i++)
        v += (r-i) * (r-i) * (h-i);
    return v;
}

void Dfs(int v, int n, int r, int h)
{
    //要搭建体积为v的n层蛋糕,最底层半径不能超过r,高度不能超过h
    if(n == 0){
        if(v) return; //层数为0体积却不为0一定失败
        else{ //搭建成功,返回最小表面积
            minArea = min(minArea, area);
            return;
        }
    }

    if(v <= 0) return; //搭建失败
    if(minV[n] > v) return; //可行性剪枝
    if(area + minA[n] >= minArea) return; //最优性剪枝
    if(h < n || r < n) return; //可行性剪枝
    if(MaxVforNRH(n, r, h) < v) return; //可行性剪枝,提前预判当前能凑出的最大体积是否小于v

    for(int rr = r; rr >= n; --rr){ //枚举底层半径
        if(n == M)
            area = rr*rr;
        for(int hh = h; hh >= n; --hh){
            area += 2*rr*hh;
            Dfs(v-rr*rr*hh, n-1, rr-1, hh-1);
            area -= 2*rr*hh;
        }
    }

}
int main()
{
    scanf("%d%d",&N,&M);
    minV[0] = 0; minA[0] = 0;
    for(int i = 1; i <= M; ++i){
        //第i层半径至少为i,高度至少为i
        minV[i] = minV[i-1] + i*i*i;
        minA[i] = minA[i-1] + 2*i*i;
    }
    if(minV[M] > N) printf("0\n"); //可行性剪枝
    else{
        int maxH = (N - minV[M-1])/(M*M)+1; //底层最大高度
        //最底层体积不超过(N - minV[M-1]),且半径至少为M
        int maxR = sqrt(double(N-minV[M-1]/M)/M) + 1; //底层高度至少为M
        area = 0;
        minArea = 1<<30;
        Dfs(N, M, maxR, maxH);
        if(minArea == 1<<30) printf("0\n");
        else printf("%d\n",minArea);
     }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/82386976
今日推荐