洛谷P1731 生日蛋糕

李煜东太神了啊啊啊啊啊!

生日蛋糕,著名搜索神题(还有虫食算)。

当年的我30分....

这哥们的程序0ms...

还有他的树网的核也巨TM神。

 

疯狂剪枝!

DFS(int d, int s, int v, int lastR, int lastH)

剪枝1.考虑在每一层内枚举H,R时的上下界。

最小肯定是当前层数d,最大呢?

考虑:

N - v = ∑hi * ri * ri(i = 1...d)

N - v >= R * R * H

R的最大值显然是H取最小的时候。

H = 1 时,R * R <= N - v

R <= sqrt(N - v)

接下来考虑H的最大:

R * R * H <= N - v

H <= (N - v) / (R * R)

剪枝2.

预处理出前 i 层的最小表面积/体积,记为mins和minv。

则 若在某一层的 v + minv[d] > N 剪枝

若在某一层的 s + mins[d] >= ans 剪枝

剪枝3.

观察 N 和 S 的公式:

N - v = ∑ri * ri * hi (i = d...M)

S - s = 2 * ∑ri * hi(i = d...M)

很相似。于是考虑得出这两个式子之间的不等关系:

rd * (S - s) >= 2 * ∑ri * ri * hi(i = d...M) = 2 * (N - v)

rd * (S - s) >= 2 * (N - v)

2 * (N - v) / rd + s <= S

如果 ans <= 2 * (N - v) / rd + s 那么 ans <= S

此时即可剪枝。

 

这样我们就得到了一个0ms的玄学搜索...

疯狂%李煜东大佬%%%

 

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N2 = 20, INF = 0x7f7f7f7f;
 6 
 7 int N, M, ans = INF, mins[N2], minv[N2];
 8 
 9 void DFS(int d, int s, int v, int lastR, int lastH) {
10     if(!d) {
11         if(v == N) {
12             ans = min(ans, s);
13         }
14         return;
15     }
16     for(int R = min(lastR - 1, (int)(sqrt(N - v))); R >= d; R--) {
17         /*if((2 * (N - v)) / R + s >= ans) { /// WA 
18             continue;
19         }*/
20         if(d == M) {
21             s = R * R;
22         }
23         for(int H = min(lastH - 1, (N - v) / (R * R)); H >= d; H--) {
24             int v2 = v + R * R * H;
25             int s2 = s + 2 * R * H;
26             if((2 * (N - v2)) / R + s2 >= ans) {
27                 continue;
28             }
29             if(s2 + mins[d - 1] >= ans) {
30                 continue;
31             }
32             if(v2 + minv[d - 1] > N) {
33                 continue;
34             }
35             DFS(d - 1, s2, v2, R, H);
36         }
37     }
38     return;
39 }
40 
41 void init(int n) {
42     for(int i = 1; i <= n; i++) {
43         mins[i] = mins[i - 1] + 2 * i * i;
44         minv[i] = minv[i - 1] + i * i * i;
45     }
46     return;
47 }
48 
49 int main() {
50     scanf("%d%d", &N, &M);
51 
52     init(M);
53 
54     DFS(M, 0, 0, INF, INF);
55 
56     printf("%d", (ans == INF) ? 0 : ans);
57     return 0;
58 }
AC代码

 

看我的注释,那个神奇的地方如果剪枝就会WA两个点,剪早了,但是不知道原理...

 

posted @ 2018-06-07 13:52  garage  阅读(77)  评论(0编辑  收藏  举报