拉格朗日乘数法

luogu2179 [NOI2012]骑行川藏

虽然我并不知道如何证明,但是背下结论套的话这题就是模板题了。

设第$i$段路骑行速度为$x_i$

$f=sum frac{s_i}{x_i}$

要求最小化$f$

设$g=E_u-sum k_i*(x_i-v_i)^2*s_i$

贪心,当$x_i<v_i$时,存在关于$v_i$对称的$x$使代价不变而答案更优
故$x_i>=v_i$

故当$g$不为0时,一定可以增大$x$使答案更优,即最优解时$g=0$

$f=sum frac{s_i}{x_i}$

$g=E_u-sum k_i*(x_i-v_i)^2*s_i=0$

设$t=f-lambda*g$

根据拉格朗日乘数啥子的,当$t$的偏导为$0$时,$f$取得极值


$-frac{s_i}{x_i}+ 2lambda k_i s_i(x_i-v_i)=0$

$x_i>=v_i$,故$lambda$随$x_i$单调

二分$lambda$使$g=0$即可

注意实数二分最好控制次数而不是$eps$

关于二分的上界我比较懵逼,我看有人开到1e5就过了,这个大概要随缘吧……

 1 //Achen
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<vector>
 7 #include<cstdio>
 8 #include<queue>
 9 #include<cmath>
10 #include<set>
11 #include<map>
12 #define Formylove return 0
13 #define For(i,a,b) for(int i=(a);i<=(b);i++)
14 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
15 #define eps 1e-15
16 const int N=1e4+7;
17 typedef long long LL;
18 typedef double db;
19 using namespace std;
20 int n;
21 db E,s[N],k[N],v[N],x[N];
22 
23 template<typename T>void read(T &x)  {
24     char ch=getchar(); x=0; T f=1;
25     while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
26     if(ch=='-') f=-1,ch=getchar();
27     for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
28 }
29 
30 int dcmp(db x) { return fabs(x)<=eps?0:(x>0?1:-1); }
31 
32 void get_x(db lmd) {
33     lmd=1.0/lmd;
34     For(i,1,n) {
35         db l=max(0.0,v[i]),r=1e5;
36         For(ti,1,80) {
37             db mid=(l+r)/2.0;
38             if(dcmp(k[i]*mid*mid*2*(mid-v[i])-lmd)<=0) l=mid;
39             else r=mid;
40         }
41         x[i]=l;
42     }
43 }
44 
45 int ck(db lmd) {
46     get_x(lmd);
47     db tp=E;
48     For(i,1,n) 
49         tp-=k[i]*(x[i]-v[i])*(x[i]-v[i])*s[i];
50     return dcmp(tp)>=0;
51 }
52 
53 int main() {
54 #ifdef ANS
55     freopen(".in","r",stdin);
56     freopen(".out","w",stdout);
57 #endif
58     read(n);
59     scanf("%lf",&E); 
60     For(i,1,n) 
61         scanf("%lf %lf %lf",&s[i],&k[i],&v[i]);
62     db l=0,r=1e5,lmd;
63     For(ti,1,80) {
64         lmd=(l+r)/2.0;
65         if(ck(lmd)) r=lmd;
66         else l=lmd;
67     }
68     get_x(lmd);
69     db ans=0;
70     For(i,1,n) ans+=s[i]/x[i];
71     printf("%.6lf
",ans);
72     Formylove;
73 }
View Code

yja

和上道题一样的模板

原文地址:https://www.cnblogs.com/Achenchen/p/9527723.html