洛谷P2179 骑行川藏
什么毒瘤...
解:n = 1的,发现就是一个二次函数,解出来一个v的取值范围,选最大的即可。
n = 2的,猜测可以三分。于是先二分给第一段路多少能量,然后用上面的方法求第二段路的最短时间。注意剩余能量不足跑完第二段路的时候,返回INF。
正解是啥拉格朗日乘子法,完全搞不倒...
1 /** 2 * There is no end though there is a start in space. ---Infinity. 3 * It has own power, it ruins, and it goes though there is a start also in the star. ---Finite. 4 * Only the person who was wisdom can read the most foolish one from the history. 5 * The fish that lives in the sea doesn't know the world in the land. 6 * It also ruins and goes if they have wisdom. 7 * It is funnier that man exceeds the speed of light than fish start living in the land. 8 * It can be said that this is an final ultimatum from the god to the people who can fight. 9 * 10 * Steins;Gate 11 */ 12 13 #include <bits/stdc++.h> 14 15 const int N = 10010; 16 17 double k[N], s[N], vv[N], E; 18 int n; 19 20 namespace n1 { 21 inline void solve() { 22 double v = vv[1] + sqrt(E / s[1] / k[1]); 23 printf("%.10f\n", s[1] / v); 24 return; 25 } 26 } 27 28 namespace n2 { 29 30 inline double cal(double v) { 31 double ans = s[1] / v; 32 double delta = k[1] * s[1] * (vv[1] - v) * (vv[1] - v); 33 //printf("E - delta = %.10f \n", E - delta); 34 double v2 = vv[2] + sqrt((E - delta) / s[2] / k[2]); 35 if(v2 < 0) return 1e14; 36 //printf("v2 %.10f = %.10f + sqrt(%.10f / %.10f / %.10f) \n", v2, vv[2], E - delta, s[2], k[2]); 37 //printf(" = %.10f + %.10f \n", vv[2], sqrt((E - delta) / s[2] / k[2])); 38 //printf("cal %.10f -> %.10f + %.10f / %.10f \n", v, ans, s[2], v2); 39 return ans + s[2] / v2; 40 } 41 42 inline void solve() { 43 44 double l = 0, r = vv[1] + sqrt(E / s[1] / k[1]); 45 for(int i = 1; i <= 100; i++) { 46 double mid = (l + r) / 2; 47 //printf("l = %.10f r = %.10f \n", l, r); 48 double ml = mid - (r - l) / 6, mr = mid + (r - l) / 6; 49 double vl = cal(ml), vr = cal(mr); 50 if(vl > vr) { 51 l = ml; 52 } 53 else { 54 r = mr; 55 } 56 } 57 printf("%.10f\n", cal(r)); 58 return; 59 } 60 } 61 62 int main() { 63 scanf("%d%lf", &n, &E); 64 for(int i = 1; i <= n; i++) { 65 scanf("%lf%lf%lf", &s[i], &k[i], &vv[i]); 66 } 67 68 if(n == 1) { 69 n1::solve(); 70 return 0; 71 } 72 73 if(n == 2) { 74 n2::solve(); 75 return 0; 76 } 77 78 return 0; 79 }
学习了一波模拟退火,突然发现这道题可能比较适合乱搞?于是开始疯狂调参最后成功在LOJ和洛谷上A掉了...
考虑如何随机化得出解。我们随机每条路的能量分配即可。
风速为负的每条路有一个能量下限。在此基础上我们把多出来的能量作为自由能量来进行分配。
初始解就是把自由能量均分...之后我们每次随机出两条路a和b,把a的若干能量给b。这里我给的能量是min(a的能量,T * c) * Rand(0, 1)
这里的c是一个参数。于是我们做到了让调整量随着温度的降低而变小。
然后瞎调一波,从0分优化到了100分......中间有很多脑洞大开改参数的过程...
最好玩的是LOJ的AC代码在洛谷上95分,洛谷的AC代码在LOJ上90分...
1 // luogu-judger-enable-o2 2 #include <bits/stdc++.h> 3 4 const int N = 10010, INF = 0x3f3f3f3f; 5 6 double k[N], s[N], vv[N], E; 7 int n; 8 9 namespace n1 { 10 inline void solve() { 11 double v = vv[1] + sqrt(E / s[1] / k[1]); 12 printf("%.10f\n", s[1] / v); 13 return; 14 } 15 } 16 17 namespace n2 { 18 19 inline double cal(double v) { 20 double ans = s[1] / v; 21 double delta = k[1] * s[1] * (vv[1] - v) * (vv[1] - v); 22 double v2 = vv[2] + sqrt((E - delta) / s[2] / k[2]); 23 if(v2 < 0) return 1e14; 24 return ans + s[2] / v2; 25 } 26 27 inline void solve() { 28 29 double l = 0, r = vv[1] + sqrt(E / s[1] / k[1]); 30 for(int i = 1; i <= 100; i++) { 31 double mid = (l + r) / 2; 32 //printf("l = %.10f r = %.10f \n", l, r); 33 double ml = mid - (r - l) / 6, mr = mid + (r - l) / 6; 34 double vl = cal(ml), vr = cal(mr); 35 if(vl > vr) { 36 l = ml; 37 } 38 else { 39 r = mr; 40 } 41 } 42 printf("%.10f\n", cal(r)); 43 return; 44 } 45 } 46 47 namespace Fire { 48 const double eps = 1e-13; 49 double T = 1, dT = 0.999992; 50 double nowE[N], temp[N], lm[N]; 51 int test[N]; 52 53 inline int rd(int l, int r) { 54 return rand() % (r - l + 1) + l; 55 } 56 57 inline double Rand() { 58 return 1.0 * rand() / RAND_MAX; 59 } 60 61 inline double calv(int i, double e) { 62 return vv[i] + sqrt(e / k[i] / s[i]); 63 } 64 65 inline double calt(int i, double e) { 66 return s[i] / (vv[i] + sqrt(e / k[i] / s[i])); 67 } 68 69 inline double init() { 70 for(int i = 1; i <= n; i++) { 71 if(vv[i] < -eps) { 72 lm[i] = k[i] * s[i] * vv[i] * vv[i]; 73 E -= lm[i]; 74 } 75 } 76 double dt = E / n, ans = 0; 77 for(int i = 1; i <= n; i++) { 78 nowE[i] = dt; 79 ans += calt(i, lm[i] + nowE[i]); 80 } 81 return ans; 82 } 83 84 inline void solve() { 85 double ans, fin = 1e14; 86 srand(69); 87 for(int A = 1; A <= 1; A++) { 88 ans = init(); 89 fin = std::min(ans, fin); 90 while(T > eps) { 91 /// Random a new solution 92 int a = rd(1, n), b = rd(1, n); 93 while(a == b) { 94 a = rd(1, n), b = rd(1, n); 95 } 96 double deltaE = std::min((long double)nowE[a], (long double)T * 1e8) * Rand(); 97 temp[a] = nowE[a] - deltaE; 98 temp[b] = nowE[b] + deltaE; 99 100 double New = ans - calt(a, lm[a] + nowE[a]) - calt(b, lm[b] + nowE[b]) 101 + calt(a, lm[a] + temp[a]) + calt(b, lm[b] + temp[b]); 102 103 fin = std::min(fin, New); 104 if(New < ans || Rand() < exp((ans - New) / T)) { 105 ans = New; 106 nowE[a] = temp[a]; 107 nowE[b] = temp[b]; 108 } 109 T = T * dT; 110 } 111 } 112 printf("%.10f\n", fin); 113 return; 114 } 115 } 116 117 int main() { 118 scanf("%d%lf", &n, &E); 119 for(int i = 1; i <= n; i++) { 120 scanf("%lf%lf%lf", &s[i], &k[i], &vv[i]); 121 } 122 123 if(n == 1) { 124 n1::solve(); 125 return 0; 126 } 127 128 if(n == 2) { 129 n2::solve(); 130 return 0; 131 } 132 133 Fire::solve(); 134 return 0; 135 }
1 #include <bits/stdc++.h> 2 3 const int N = 10010, INF = 0x3f3f3f3f; 4 5 double k[N], s[N], vv[N], E; 6 int n; 7 8 namespace n1 { 9 inline void solve() { 10 double v = vv[1] + sqrt(E / s[1] / k[1]); 11 printf("%.10f\n", s[1] / v); 12 return; 13 } 14 } 15 16 namespace n2 { 17 18 inline double cal(double v) { 19 double ans = s[1] / v; 20 double delta = k[1] * s[1] * (vv[1] - v) * (vv[1] - v); 21 double v2 = vv[2] + sqrt((E - delta) / s[2] / k[2]); 22 if(v2 < 0) return 1e14; 23 return ans + s[2] / v2; 24 } 25 26 inline void solve() { 27 28 double l = 0, r = vv[1] + sqrt(E / s[1] / k[1]); 29 for(int i = 1; i <= 100; i++) { 30 double mid = (l + r) / 2; 31 //printf("l = %.10f r = %.10f \n", l, r); 32 double ml = mid - (r - l) / 6, mr = mid + (r - l) / 6; 33 double vl = cal(ml), vr = cal(mr); 34 if(vl > vr) { 35 l = ml; 36 } 37 else { 38 r = mr; 39 } 40 } 41 printf("%.10f\n", cal(r)); 42 return; 43 } 44 } 45 46 namespace Fire { 47 const double eps = 1e-13; 48 double T = 1000, dT = 0.99999; 49 double nowE[N], temp[N], lm[N]; 50 int test[N]; 51 52 inline int rd(int l, int r) { 53 return rand() % (r - l + 1) + l; 54 } 55 56 inline double Rand() { 57 return 1.0 * rand() / RAND_MAX; 58 } 59 60 inline double calv(int i, double e) { 61 return vv[i] + sqrt(e / k[i] / s[i]); 62 } 63 64 inline double calt(int i, double e) { 65 return s[i] / (vv[i] + sqrt(e / k[i] / s[i])); 66 } 67 68 inline double init() { 69 for(int i = 1; i <= n; i++) { 70 if(vv[i] < -eps) { 71 lm[i] = k[i] * s[i] * vv[i] * vv[i]; 72 E -= lm[i]; 73 } 74 } 75 double dt = E / n, ans = 0; 76 for(int i = 1; i <= n; i++) { 77 nowE[i] = dt; 78 ans += calt(i, lm[i] + nowE[i]); 79 } 80 return ans; 81 } 82 83 inline void solve() { 84 double ans, fin = 1e14; 85 srand(2332); 86 for(int A = 1; A <= 1; A++) { 87 ans = init(); 88 fin = std::min(ans, fin); 89 while(T > eps) { 90 /// Random a new solution 91 int a = rd(1, n), b = rd(1, n); 92 while(a == b) { 93 a = rd(1, n), b = rd(1, n); 94 } 95 double deltaE = std::min((long double)nowE[a], (long double)T * 1e11) * Rand(); 96 temp[a] = nowE[a] - deltaE; 97 temp[b] = nowE[b] + deltaE; 98 99 double New = ans - calt(a, lm[a] + nowE[a]) - calt(b, lm[b] + nowE[b]) 100 + calt(a, lm[a] + temp[a]) + calt(b, lm[b] + temp[b]); 101 102 fin = std::min(fin, New); 103 if(New < ans || Rand() < exp((ans - New) / T)) { 104 ans = New; 105 nowE[a] = temp[a]; 106 nowE[b] = temp[b]; 107 } 108 T = T * dT; 109 } 110 } 111 printf("%.8f\n", fin); 112 return; 113 } 114 } 115 116 int main() { 117 scanf("%d%lf", &n, &E); 118 for(int i = 1; i <= n; i++) { 119 scanf("%lf%lf%lf", &s[i], &k[i], &vv[i]); 120 } 121 122 if(n == 1) { 123 n1::solve(); 124 return 0; 125 } 126 127 if(n == 2) { 128 n2::solve(); 129 return 0; 130 } 131 132 Fire::solve(); 133 return 0; 134 }
调参心得:△T越接近1,就越慢,同时效果越好。初始温度太大可能会超时...