普及一下拉格朗日乘数法。
知识链接
问题:求
f(a,b,c,...)
的极值,其中
g(a,b,c,...)=0
。
即一个多元式在某一限制下的极值。
令
f+λg=0
。
于是有:
f‘(a)+λg‘(a)=0
f‘(b)+λg‘(b)=0
......
g(a,b,c,...)=0
有
n
个方程,
对于此题,拉格朗日的方程为:
−siv2i+2λkisi(vi−vi‘)=0
化简一下:
2λkiv2i(vi−vi‘)=1
二分一下 λ ,然后求出每一个方程的解检验一下即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#define eps 1e-12
#define MAXN 10005
using namespace std;
int n;
double s[MAXN], k[MAXN], v[MAXN], E, vv[MAXN], vmax[MAXN];
double cal(double num)
{
double sum=0, l, r, mid, a, b;
for(int i=1;i<=n;++i)
{
l=v[i], r=vmax[i];
a=2*num*k[i], b=-a*v[i];
while(l+eps<r)
{
mid=(l+r)/2;
if(a*mid*mid*mid+b*mid*mid-1>0)r=mid;
else l=mid;
}
vv[i]=(l+r)/2;
sum+=s[i]*k[i]*(vv[i]-v[i])*(vv[i]-v[i]);
}
return sum;
}
int main()
{
scanf("%d%lf",&n,&E);
for(int i=1;i<=n;++i)
{
scanf("%lf%lf%lf",&s[i],&k[i],&v[i]);
vmax[i]=s[i]>0?v[i]+sqrt(E/s[i]/k[i]):0;
}
double l=0, r=1e4, mid;
while(r-l>eps)
{
mid=(l+r)/2;
if(cal(mid)<E)r=mid;
else l=mid;
}
double ans=0;
for(int i=1;i<=n;++i)ans+=s[i]/vv[i];
printf("%.10f\n",ans);
return 0;
}