拉格朗日乘数法 ACdreamers
[Math & Algorithm] 拉格朗日乘数法
首先那个能量肯定是要花完的,就变成一个限制了,乘上拉格朗日乘子,求偏导,变成了
2λkix2i(xi−vi)=1
∑kisi(xi−vi)2=E
发现 x≥v 且 x2(x−v) 是单调增的,那么二分 λ 就好了
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef long double ld;
const int N=10005;
const ld eps=1e-12;
int n; ld E,s[N],k[N],V[N];
ld v[N];
inline ld Calc(ld mid){
ld ret=0;
for (int i=1;i<=n;i++){
ld L=max(V[i],(ld)0.0),R=1e9,MID;
while (R-L>eps){
MID=(L+R)/2;
if (2*mid*k[i]*MID*MID*(MID-V[i])>1.0)
R=MID;
else
L=MID;
}
v[i]=(L+R)/2;
ret+=k[i]*s[i]*(v[i]-V[i])*(v[i]-V[i]);
}
return ret;
}
int main(){
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
cin>>n>>E;
for (int i=1;i<=n;i++) cin>>s[i]>>k[i]>>V[i];
ld L=0,R=1e9,MID;
while (R-L>eps){
ld ret=Calc(MID=(L+R)/2);
if (ret>=E)
L=MID;
else
R=MID;
}
Calc((L+R)/2);
ld ans=0;
for (int i=1;i<=n;i++)
ans+=s[i]/v[i];
printf("%.10lf\n",(double)ans);
return 0;
}