POJ - 1190 生日蛋糕(dfs+剪枝)
題目鏈接:點擊查看
題目大意:給出生日蛋糕的總體積,以及有多少層,我們要求下面的每一層的高度和半徑都要比上面一層的大,并且都是整數,求滿足要求的最小表面積
題目分析:因為題目提到了半徑和高度都是整數,所以我們可以想到枚舉或者搜索,只要想到高度和半徑的上下限就可以設計dfs了,為了方便計算,可以將體積以及面積的計算公式中的π全都約掉,避免出現小數丟失精度,我們已經知道了這個題目中的N最大是1e4,所以當半徑為1的時候,高度的最大值就是1e4,而當高度為1的時候,半徑的最大值是100,最小值也很簡單,因為題目中要求了每一層的高度和半徑都必須必下面一層大,那么我們可以從最上面的一層開始枚舉,這樣每一層的最小值就是所枚舉的層數了,有了枚舉上限和下限,我們就能很輕松的寫出一個帶有最優性剪枝的代碼了:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;//const int N=1e4+100;int N,M;int ans;void dfs(int cnt,int V,int S,int r,int h) {if(S>=ans)//最優性剪枝return;if(cnt==0)//枚舉到達邊界,判斷能否更新答案{if(V==N)ans=S;return;}for(int i=r-1;i>=cnt;i--)//枚舉半徑{if(cnt==M)S=i*i;//算上頂部面積for(int j=h-1;j>=cnt;j--)//枚舉高dfs(cnt-1,V+i*i*j,S+2*i*j,i,j); } }int main() { // freopen("input.txt","r",stdin);while(scanf("%d%d",&N,&M)!=EOF){ans=inf;dfs(M,0,0,100,10000);printf("%d\n",ans);}return 0; }不過很可惜,這份代碼的剪枝還不足以AC,所以T掉了
我們應該考慮該如何剪枝,因為在每一層都有100*10000種情況需要枚舉,而我們共有20層,所以也就是2e7的時間復雜度,肯定是需要繼續剪枝的
我們可以初始化維護一個每一層所可以到達的最小面積和最小體積,若當前的面積加上最小的面積已經超過了最優解,或者當前體積加上最小體積超過了題目給出的N,那么也可以直接返回了
在我們枚舉的時候,因為最外層枚舉的是半徑,到了內層后,我們就可以通過半徑大體算出一個高度的上限,這樣也可以做到一定的剪枝的效果
最后,也是最重要的一個剪枝,是我從網上看到的,思路大概是這樣:若我們將剩下所有的體積都做成一個圓柱體,那么這樣貢獻的表面積無疑是最小的,舉個例子,相同的表面積,我們如果是一個圓柱體,和好多個圓柱體摞起來組成的一個錐形對比,肯定是錐形的體積更小,如果這個最小的表面積加上當前的表面積都大于了最優解,就可以直接回溯了
不過很納悶的一件事是,上面的剪枝都已經沒問題了,就是枚舉的順序,我一開始是從半徑和高度的最小值枚舉到最大值的,一直T,隨后改成了從最大值枚舉到最小值,一下就A了,還跑了0ms,我就想著會不會是因為答案都在很大的時候才出現,抱著這個想法我就把上面那個代碼改了一下,結果在本地都跑不出來。。
挺玄學的一道題,上代碼吧:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;//const int N=1e4+100;int N,M;int ans;int minS[25],minV[25];void dfs(int cnt,int V,int S,int r,int h) {if(S>=ans)//最優性剪枝return;if(cnt==0)//判斷能否更新答案{if(V==N)ans=S;return;}if(S+minS[cnt]>ans||V+minV[cnt]>N)//若當前結果加上最小表面積/最小體積都不滿足條件,直接回溯return;if((N-V)*2/r+S>=ans)//若剩余體積全部轉化為一個圓柱體所得出的表面積,加上現在的結果都大于最優解,回溯return;for(int i=r-1;i>=cnt;i--)//枚舉半徑{if(cnt==M)S=i*i;//算上頂部面積int mmax=min((N-V-minV[cnt-1])/(i*i),h-1);//利用已經確定的半徑限制一下高度for(int j=mmax;j>=cnt;j--)//枚舉高度dfs(cnt-1,V+i*i*j,S+2*i*j,i,j); } }void init() {minV[0]=minS[0]=0;for(int i=1;i<25;i++){minV[i]=minV[i-1]+i*i*i;//維護每層最小的體積minS[i]=minS[i-1]+i*i*2;//維護每層最小的表面積} }int main() { // freopen("input.txt","r",stdin);init();while(scanf("%d%d",&N,&M)!=EOF){ans=inf;dfs(M,0,0,100,10000);if(ans==inf)ans=0;printf("%d\n",ans);}return 0; }?
總結
以上是生活随笔為你收集整理的POJ - 1190 生日蛋糕(dfs+剪枝)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POJ - 2480 Longge's
- 下一篇: POJ - 2248 Addition