1. 程式人生 > >【洛谷P1731】生日蛋糕

【洛谷P1731】生日蛋糕

前綴和 題目 () 制作 fin acm 生日蛋糕 ima using

題目背景

7月17日是Mr.W的生日,ACM-THU為此要制作一個體積為Nπ的M層

生日蛋糕,每層都是一個圓柱體。

設從下往上數第i(1<=i<=M)層蛋糕是半徑為Ri, 高度為Hi的圓柱。當i<M時,要求Ri>Ri+1且Hi>Hi+1。

由於要在蛋糕上抹奶油,為盡可能節約經費,我們希望蛋糕外表面(最下一層的下底面除外)的面積Q最小。

令Q= Sπ

請編程對給出的N和M,找出蛋糕的制作方案(適當的Ri和Hi的值),使S最小。

(除Q外,以上所有數據皆為正整數)

題目描述

技術分享

輸入輸出格式

輸入格式:

有兩行,第一行為N(N<=20000),表示待制作的蛋糕的體積為Nπ;第二行為M(M<=15),表示蛋糕的層數為M。

輸出格式:

僅一行,是一個正整數S(若無解則S=0)。

輸入輸出樣例

輸入樣例#1:
100
2
輸出樣例#1:
68

題解:

搜索+剪枝

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int INF=0x7fffffff;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    
while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();} while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} return x*f; } int n,m; int minn[20],maxx[20][200][200]; int ans=INF; void dfs(int k,int r,int h,int v,int s) { if(n-v<minn[k]) return; if(v+m-k>n) return; if(v+(h-1
)*(r-1)*(r-1)*(m-k)<n) return; if(2*(n-v)/r+s>=ans) return; if(k==m) { if(v==n) ans=s; return; } for(int i=m-k;i<=r-1;i++) for(int j=m-k;j<=h-1;j++) { if(s+2*i*j>=ans) break; dfs(k+1,i,j,v+i*i*j,s+2*i*j); } } int main() { n=read();m=read(); int tmp=0; for(int i=1;i<=m;i++) { tmp=tmp+i*i*i; minn[m-i]=tmp; } for(int i=m;i*i<=n;i++) for(int j=m;j*i*i<=n;j++) dfs(1,i,j,i*i*j,2*i*j+i*i); if(ans==INF) printf("0\n"); else printf("%d\n",ans); return 0; }

不知道這種為什麽這麽快!

#include<cstdio>
#include<cmath>
#include<algorithm>
#define oo 2147483647
using namespace std;
int NN,M,N,ans=oo;
int ss[21],sv[21];
inline void dfs(int t,int S,int V,int lR,int lH)//層數,已用總面積,已用總體積,上一層半徑 ,上一層高度
{
    if (t==0)
    {
        if (V==N) ans=min(ans,S);
        return ;
    }
    if (V+sv[t]>N) return ;//分別對應上述3個剪枝,其中2、3是最優化剪枝 
    if (S+ss[t]>ans) return ;
    if (S+2*(N-V)/lR>ans) return ;
    for (int r=lR-1;r>=t;r--)
    {
        if (t==M) S=r*r;
        int maxh=min((N-V-sv[t-1])/(r*r),lH-1);
        for (int h=maxh;h>=t;h--)
        {
            dfs(t-1,S+2*r*h,V+r*r*h,r,h);
        }
    }
}
int main()
{
    scanf("%d %d",&N,&M);
    for (int i=1;i<=M;i++)
    {
        ss[i]=2*i*i;//計算出每一層的最小側面積 
        ss[i]+=ss[i-1];//要求前綴和 
        sv[i]=i*i*i;//最小體積 
        sv[i]+=sv[i-1];//要求前綴和 
    }
    dfs(M,0,0,sqrt(N),N);//第一層的高度最小為1,所以半徑最大就是根號N 
    if (ans==oo) printf("0");
    else printf("%d",ans);
    return 0;
}

  

【洛谷P1731】生日蛋糕