【洛谷2179】[NOI2012] 骑行川藏(拉格朗日乘数法)

点此看题面

  • 给定(n)对参数(s_i,k_i,v_i'),要求满足(v_ige v_i',sum_{i=1}^nk_i(v_i-v_i')^2s_ile E),并最小化(sum_{i=1}^nfrac{s_i}{v_i})
  • (nle10^4)

拉格朗日乘数法

一个非常诡异的数学问题。

考虑我们让(v_i)取大肯定更优,因此(sum_{i=1}^nk_i(v_i-v_i')^2s_i)必然会卡到上界(E),所以限制条件就由不等式被化为了等式。

这是一个比较经典的约束极值问题,定义:

[f(v)=sum_{i=1}^nfrac{s_i}{v_i}\ varphi(v)=sum_{i=1}^nk_i(v_i-v_i')^2s_i-E ]

现在的问题就是限制(varphi(v)=0),求(f(v))的最小值。

(varphi(v))乘上一个参数(lambda)(f(v))相加得到:

[sum_{i=1}^nfrac{s_i}{v_i}+lambda(sum_{i=1}^nk_i(v_i-v_i')^2s_i-E) ]

我们对于这个式子求偏导,并要求导数全部等于(0),得到:

[egin{cases} forall iin[1,n],frac{s_i}{-v_i^2}+2lambda k_is_i(v_i-v_i')=0\ sum_{i=1}^nk_i(v_i-v_i')^2s_i-E=0 end{cases} ]

其中第一组式子可以约去(s_i)并改写成:

[2lambda k_i(v_i-v_i')v_i^2=1 ]

二分答案

(2lambda k_i(v_i-v_i')v_i^2=1)发现,显然(lambda)越大(v_i)越小,那么(varphi(v))也就越小。

所以说,我们可以二分(lambda),计算出所有的(v_i),然后把(varphi(v))(0)比较即可。

而要求(v_i),由于(v_ige v_i'),发现(2lambda k_i(v_i-v_i')v_i^2)是关于(v_i)递增的,因此同样可以二分答案与(1)比较。

于是这道题就做完了。

代码:(O(nlog^2V))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10000
#define DB double
#define eps 1e-12
using namespace std;
int n;DB E,s[N+5],k[N+5],v0[N+5];
DB v[N+5];I bool Check(Con DB& x)//检验λ
{
	DB l,r,mid,t=0;for(RI i=1;i<=n;t+=k[i]*(v[i]-v0[i])*(v[i]-v0[i])*s[i],++i)//t统计φ(v)
		{l=max(v0[i],0.0),r=1e6;W(r-l>eps) mid=(l+r)/2,2*x*k[i]*(mid-v0[i])*mid*mid>=1?v[i]=r=mid:l=mid;}//二分v[i]
	return t<=E;//比较φ(v)与0
}
int main()
{
	RI i;for(scanf("%d%lf",&n,&E),i=1;i<=n;++i) scanf("%lf%lf%lf",s+i,k+i,v0+i);
	DB l=0,r=1e6,mid;W(r-l>eps) Check(mid=(l+r)/2)?r=mid:l=mid;//二分λ
	DB t=0;for(Check(r),i=1;i<=n;++i) t+=s[i]/v[i];return printf("%.8lf
",t),0;//计算答案
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2179.html