Luogu P1731 生日蛋糕(dfs剪枝)

 
#include<cstdio>
#include<cmath>
using namespace std;

int n,m,r[55],h[55],minn=0x7ffffff;

inline int min(int x,int y){return x>y?y:x;}

inline void dfs(int now,int rest,int s,int z){
    if(rest==0&&now==m+1){
        s+=r[1]*r[1];
        minn=min(minn,s);
        return;
    }
    if(rest==0||now==m+1)return;
    if(s+r[1]*r[1]>=minn)return;//剪枝
    if(r[now-1]*r[now-1]*h[now-1]*z<rest)return;//剪枝
    for(int i=r[now-1]-1;i>=z;i--){
        for(int j=h[now-1]-1;j>=z;j--){
            if(i*i*j<=rest){
                r[now]=i;
                h[now]=j;
                dfs(now+1,rest-i*i*j,s+(2*i*j),z-1);
                r[now]=0;
                h[now]=0;
            }
        }
    }
}

int main(){
    /*
  for r[1] h[1]:
    v=r*r*h
    v<n r>=m h>=m .`. r*r>=m*m 
     h=v/(r*r)  .`. h max=vmax/(r*r)min=n/(m*m)  
    r*r=v/h  .`. r*r max=vmax/hmin=n/m
  */
    scanf("%d%d",&n,&m);
    r[0]=int(sqrt(n/m))+1;
    h[0]=n/(m*m)+1;
    dfs(1,n,0,m);
    if(minn==0x7ffffff)printf("%d
",0);
    else printf("%d
",minn);
}
1.代码参考洛谷一位巨佬的题解,不过ta有几个地方很莫名其妙O_O ,于是我改动了一些东西。

2.例如,r[0]与h[0]的值应该分别取(int)sqrt(n/m)与n/(m*m),如果都取sqrt(n)的话,有点不太好理解。手推的过程在注释里面。
但为什么我要分别加上1呢?难道不是更费吗?
起因是这样的:如果你看过洛谷题解,你一定会发现,大部分人的程序,当n和m较小时,都输出0,即没搜到。
根据洛谷上另一个巨佬的评论,你会发现,这是精度问题导致的。
(其实我觉得对于我的r[0]与h[0],当m=1时还有一个原因可能是r[1]h[1]有最大值r[0]h[0],而不是趋近最大值r[0]h[0],而我们在dfs中r[1]初值是r[0]-1)
因此,像这样稍微扩大一下搜索范围,对于1 1这组很小的数据就可以顺利输出3了。
而这一微小的鬼畜操作并不会对较大测试点造成多大影响。
(其实我发现如果r[0]与h[0]取得过大,例如n,会全输出0 QAQ 哪位巨佬可以告诉我呀?)

3.其实dfs的第三个if中,s+r[1]*r[1]>=minn是return的必要不充分条件,因为没有记上之后拼的侧面积。
因此,为了让它更快,或许也可以往左边式子里面加上一些鬼畜的玩意,例如加上z,2*z,2*z*z等等。
(好吧我一开始就是洛谷巨佬题解的这个地方没看懂)

4.好吧这题实在是太神奇了,我在洛谷上交了22次以上QAQ

5.参考文献:https://www.luogu.org/blog/zzmblog/solution-p1731
原文地址:https://www.cnblogs.com/Y15BeTa/p/luogu1731.html