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

点此看题面

  • 给定\(n\)对参数\(s_i,k_i,v_i'\),要求满足\(v_i\ge v_i',\sum_{i=1}^nk_i(v_i-v_i')^2s_i\le E\),并最小化\(\sum_{i=1}^n\frac{s_i}{v_i}\)。
  • \(n\le10^4\)

拉格朗日乘数法

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

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

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

\[f(v)=\sum_{i=1}^n\frac{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}^n\frac{s_i}{v_i}+\lambda(\sum_{i=1}^nk_i(v_i-v_i')^2s_i-E) \]

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

\[\begin{cases} \forall i\in[1,n],\frac{s_i}{-v_i^2}+2\lambda 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\)并改写成:

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

二分答案

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

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

而要求\(v_i\),由于\(v_i\ge v_i'\),发现\(2\lambda 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\n",t),0;//计算答案
}
上一篇:css3新增的属性


下一篇:【css】animate.css使用指南