生日蛋糕
题面:https://www.luogu.org/problemnew/show/P1731
剪枝:
可行性剪枝:
1.为了保证严格大于,并且是整数,所以每一层的h,r最小是m-now+1
2.如果最后都选择能选的最大的,比n还小,return
3.如果最后都选择能选的最小的(这个可以预处理),比n还大,return
最优性剪枝:
1.如果S>ans,return
2.如果最后都选择表面积最小的(这个可以预处理),比ans还大return
公式可以手动推。
顺序:
1.倒序能比较快的达到上界。(正序会TLE)
剪枝还是比较好想的。
(但是第一遍全想到了,但是没有写全)
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=20000+5; const ll inf=(1LL*1<<61); int n,m; ll ans=inf; int sz[20],biao[20]; bool che(int now,ll S,ll V,ll r,ll h){ ll msz=V+sz[m-now+1]; if(msz>n) return false; ll mib=S+biao[m-now+1]; if(mib>ans) return false; ll mxz=V; h--,r--; for(int i=now;i<=m;i++){ mxz+=h*r*r; h--,r--; } if(mxz<n) return false; return true; } void dfs(int now,ll S,ll V,ll r,ll h){ if(S>=ans) return; if(!che(now,S,V,r,h)) return; //cout<<now<<" : "<<S<<" "<<V<<" las "<<r<<" and "<<h<<endl; //if(r==-1||h==-1) return; if(now==m+1){ if(V==n){ ans=min(ans,S); } return; } if(now==1){ for(ll i=sqrt(n);i>=m;i--){ for(ll j=n/(i*i);j>=m;j--){ //if(!che(now+1,i*i+2*i*j,j*i*i))break; dfs(now+1,i*i+2*i*j,j*i*i,i,j); } } } else if(now<m){ for(ll i=r-1;i>=m-now+1;i--){ for(ll j=h-1;j>=m-now+1;j--){ //if(!che(now+1,S+2*i*j,V+i*i*j)) break; dfs(now+1,S+2*i*j,V+i*i*j,i,j); } } } else if(now==m){ int re=n-V; if(re<0) return; for(ll i=r-1;i>=m-now+1;i--){ if(re/(i*i)>=h) break; if(re%(i*i)==0){ if(re/(i*i)<h){ int hh=re/(i*i); dfs(now+1,S+2*hh*i,n,i,hh); } } } } } int main(){ ans=inf; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ sz[i]=sz[i-1]+i*i*i; biao[i]=biao[i-1]+2*i*i; } dfs(1,0,0,n+1,n+1); printf("%lld",ans==inf?0:ans);return 0; }