本文主要是介绍【NOI2012】骑行川藏(拉格朗日乘数法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
传送门
- l i m i t s : φ ( v 1 , v 2 , … , v n ) = ∑ k i ( v i − v i ′ ) 2 s i = E m i n i m i z e { f ( v 1 , v 2 , … , v n ) = ∑ s i v i } L ( v 1 , v 2 , … , v n ) = f + λ φ limits:\varphi(v_1,v_2,\dots,v_n)=\sum k_i(v_i-v_i')^2s_i=E\\ minimize\{f(v_1,v_2,\dots,v_n)=\sum \frac{s_i}{v_i}\}\\ L(v_1,v_2,\dots,v_n)=f+\lambda \varphi limits:φ(v1,v2,…,vn)=∑ki(vi−vi′)2si=Eminimize{f(v1,v2,…,vn)=∑visi}L(v1,v2,…,vn)=f+λφ
- l i m i t limit limit 取等是因为最优解法一定把能量用完,需要满足:
{ ∂ L ∂ v i = − s i v i 2 + 2 λ ( v i − v i ′ ) k i s i = 0 ( 1 ≤ i ≤ n ) ∑ k i ( v i − v i ′ ) s i = E \left\{ \begin{aligned} \frac{\partial L}{\partial v_i}=-\frac{s_i}{v_i^2}+2\lambda(v_i-v_i')k_is_i=0(1\le i\le n)\\ \sum k_i(v_i-v_i')s_i=E \end{aligned} \right. ⎩⎪⎨⎪⎧∂vi∂L=−vi2si+2λ(vi−vi′)kisi=0(1≤i≤n)∑ki(vi−vi′)si=E
那么直接二分 λ \lambda λ 就做完了
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs double eps = 1e-12;
cs int N = 1e4 + 50;
int n; double E, s[N], k[N], v[N], a[N];
double sqr(double a){ return a * a; }
double clc(double v, double v0, double k){ return k*v*v*(v-v0)*2; }
double chk(double lambda){double sm=0;for(int i=1; i<=n; i++){double l=max(0.0,v[i]), r=1e5;while(r-l>eps){double mid=(l+r)/2;if(lambda*clc(mid,v[i],k[i])>=1) r=mid; else l=mid;} a[i]=l; sm=sm+k[i]*sqr(a[i]-v[i])*s[i];} return sm;
}
double work(){ double as=0; for(int i=1; i<=n; i++) as=as+s[i]/a[i]; return as; }
int main(){scanf("%d%lf",&n,&E);for(int i=1; i<=n; i++)scanf("%lf%lf%lf",&s[i],&k[i],&v[i]);double l=0, r=1e5;while(r-l>eps){double mid=(l+r)/2;if(chk(mid)>E) l=mid; else r=mid;} printf("%.8lf",work()); return 0;
}
这篇关于【NOI2012】骑行川藏(拉格朗日乘数法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!