前置芝士:拉格朗日乘子法
要求(n)元目标函数(f(x_1,x_2,...,x_n))的极值,且有(m)个约束函数形如(h_i(x_1,x_2,...,x_n)=0)
引入松弛变量(alpha _1-alpha _m),构造拉格朗日函数如下:
然后分别对(x)和(a)求偏导并令偏导值为(0)($ abla $为梯度向量):
求解上述方程组,即可求得极值点。但是解方程组的代价太大了,在做题时我们一般会通过函数的单调性二分来解
为什么可以这样呢,考虑一下,满足条件的极值点应该是在目标函数的等高线与约束函数曲线相切的点,在这一点上有如下等式成立:
而拉格朗日函数求导之后和上式本质相同,因此它能求得最值
还有广义拉格朗日乘子法是适用于有不等式约束的情况
题解
首先我们把目标函数和约束函数都找出来
using namespace std;
define N 10000
const double eps = 1e-13, INF = 1e5;
int n;
double Eu, s[N + 5], k[N + 5], v0[N + 5], v[N + 5];
bool check(double lamda) {
for (int i = 1; i <= n; ++i) {
double tar = 1 / (2 * k[i] * lamda), l = max(v0[i], 0.0), r = INF, mid;
while(r - l >= eps) {
mid = (l + r) / 2;
if (mid * mid * (mid - v0[i]) > tar) r = mid;
else l = mid;
}
v[i] = mid;
}
double E = 0;
for (int i = 1; i <= n; ++i)
E += k[i] * s[i] * pow(v[i] - v0[i], 2);
return E <= Eu;
}
int main() {
cin >> n >> Eu;
for (int i = 1; i <= n; ++i)
cin >> s[i] >> k[i] >> v0[i];
double l = 0, r = INF, mid;
while (r - l >= eps) {
mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
double ans = 0;
for (int i = 1; i <= n; ++i)
ans += s[i] / v[i];
cout << setiosflags(ios::fixed) << setprecision(10);
cout << ans << endl;
return 0;
}