Address
https://www.lydsy.com/JudgeOnline/problem.php?id=2876
Solution
为了方便某些萌新,讲下拉格朗日乘数法。
作用:求多元函数
f(x1,x2,...,xn)
f
(
x
1
,
x
2
,
.
.
.
,
x
n
)
在约束
ϕ(x1,x2,...,xn)=0
ϕ
(
x
1
,
x
2
,
.
.
.
,
x
n
)
=
0
下的极值。引入变量
λ
λ
,令拉格朗日函数:
对 F F 的每个变量(包括 )分别求偏导。
(事实上对 λ λ 求偏导后的结果就是 ϕ(x1,x2,...,xn) ϕ ( x 1 , x 2 , . . . , x n ) )
(多元函数 F F 对一个自变量 的偏导数,定义为将 F F 除 之外的自变量全部视为常数后对 F F 求导)
当 对每个自变量的偏导数都等于 0 0 时,
在约束 ϕ(x1,x2,...,xn)=0 ϕ ( x 1 , x 2 , . . . , x n ) = 0 下取得极值。
回到原问题。
发现体力不是一个标准的约束。
但如果体力没用完,则一定可以在某一天速度更快。
所以, 存在一种最优方案使得体力恰好用完。
同样我们也能得到,存在一种最优方案使得对于每个 i i 有 。
( vi v i 表示第 i i 天实际速度)
目标函数:
约束条件:
引入系数 λ λ ,构造拉格朗日函数:
对 vi v i 求偏导得到:
要求上式为 0 0 。
易得上式左边在 vi≥v′i v i ≥ v i ′ 即上式左边的值大于等于 0 0 时单调递增。
故在 确定时二分答案可以算出 vi v i 。
同时, λ λ 越大, vi v i 越小。
所以 λ λ 的值也可以二分,通过 ϕ(v1,v2,...,vn) ϕ ( v 1 , v 2 , . . . , v n ) 的值来调整 λ λ 。
最后得出了 λ λ 的值之后就能计算取到极值时的 vi v i 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
const int N = 1e4 + 5;
const double eps = 1e-12;
int n;
double eu, k[N], s[N], vp[N], ans;
double vsol(double lambda, int i) {
double l = max(vp[i], 0.0), r = 1e5;
while (r - l > eps) {
double mid = (l + r) / 2, val;
val = 2.0 * lambda * k[i] * (mid - vp[i]) * mid * mid;
if (val < 1) l = mid;
else r = mid;
}
return (l + r) / 2;
}
double calc(double lambda) {
int i; double res = 0, tmp;
For (i, 1, n) tmp = vsol(lambda, i),
res += k[i] * s[i] * (tmp - vp[i]) * (tmp - vp[i]);
return res;
}
int main() {
int i;
double x, y, lambda, l = 0, r = 1e5;
cin >> n >> eu;
For (i, 1, n) scanf("%lf%lf%lf", &s[i], &k[i], &vp[i]);
while (r - l > eps) {
double mid = (l + r) / 2;
if (calc(mid) > eu) l = mid;
else r = mid;
}
For (i, 1, n) ans += s[i] / vsol((l + r) / 2, i);
printf("%.10lf\n", ans);
return 0;
}