洛谷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 }
看我的注释,那个神奇的地方如果剪枝就会WA两个点,剪早了,但是不知道原理...