1. 程式人生 > >[BZOJ2876]騎行川藏

[BZOJ2876]騎行川藏

拉格朗日 拉格朗日乘數 方程 ots tdi info 導致 part 拆分

以前並沒有發現微積分教材上有這種東西...我還是太菜了...

其實就是要在滿足$\sum\limits_{i=1}^nk_is_i(v_i-v_i‘)^2\leq E$的同時求$\sum\limits_{i=1}^n\dfrac{s_i}{v_i}$的最小值

首先我們要跑得盡可能快,所以$v_i\geq v_i‘$,而且在最優解體能是一定會被用完的,那麽限制就變成等式了

拉格朗日乘數法可用於求多元函數的帶限制極值:$g(x_1,\cdots,x_n)=0$,求$f(x_1,\cdots,x_n)$的極值

我看的書上有一個挺好的幾何解釋:把$f$的圖像畫出來,再在上面畫一些“等高線”,同時把$g(x_1,\cdots,x_n)=0$和$f(x_1,\cdots,x_n)$的“交線”畫出來,那麽取到極值的地方就是等高線與交線相切的地方

比如說求$f(x,y)=x^2+y^2+2$在限制$g(x,y)=x^2+\dfrac14y^2-1=0$下的極值,三張圖一目了然

技術分享圖片黃色:$z=f(x,y)$,紅色:$g(x,y)=0$

技術分享圖片$z=2$

技術分享圖片$z=6$

相切意味著梯度線性相關,即是說如果在$x_i=t_i$處$f$取得極值,那麽$\nabla f(t_1,\cdots,t_n)=\lambda\nabla g(t_1,\cdots,t_n)$,我們把它拆分成關於每個變量的偏導,即對於$\forall1\leq i\leq n$有$\left.\dfrac{\partial f}{\partial x_i}\right|_{x_i=t_i}=\lambda\left.\dfrac{\partial g}{\partial x_i}\right|_{x_i=t_i}$

再加上$g(t_1,\cdots,t_n)=0$,總共$n+1$個變量和$n+1$條方程,可以解出來

再看這道題,限制條件是$\sum\limits_{i=1}^nk_is_i(v_i-v_i‘)^2-E=0$,我們要求$\sum\limits_{i=1}^n\dfrac{s_i}{v_i}$的極值,所以有方程$-\dfrac1{v_i^2}=2\lambda k_i(v_i-v_i‘)$,首先這說明$\lambda\lt0$,方程左邊是經過三四象限的類雙曲線,右邊是斜率為負的經過第一象限的直線,所以當$\lambda$確定後有且只有一個$v_i$滿足方程,並且因為$\lambda$越大,$v_i$也越大,這直接導致了$\sum\limits_{i=1}^nk_is_i(v_i-v_i‘)^2$變大,所以我們可以二分出滿足關於$E$的限制的$\lambda$,在這個過程中求$v_i$也是可以二分的,於是我們就愉悅地做完了這題

註意精度...

#include<stdio.h>
typedef double du;
const du eps=1e-14,inf=1e9;
du s[10010],k[10010],v[10010];
int n;
du sqr(du x){return x*x;}
du calc(int i,du lm){
	du l,r,mid;
	l=eps;
	r=inf;
	while(r-l>eps){
		mid=(l+r)*.5;
		if(-1<2*lm*k[i]*(mid-v[i])*sqr(mid))
			l=mid;
		else
			r=mid;
	}
	return mid;
}
du check(du lm){
	int i;
	du r=0;
	for(i=1;i<=n;i++)r+=k[i]*s[i]*sqr(calc(i,lm)-v[i]);
	return r;
}
int main(){
	int i;
	du E,l,r,mid,ans;
	scanf("%d%lf",&n,&E);
	for(i=1;i<=n;i++)scanf("%lf%lf%lf",s+i,k+i,v+i);
	l=-inf;
	r=-eps;
	while(r-l>eps){
		mid=(l+r)*.5;
		if(check(mid)<E)
			l=mid;
		else
			r=mid;
	}
	ans=0;
	for(i=1;i<=n;i++)ans+=s[i]/calc(i,mid);
	printf("%.10lf",ans);
}

[BZOJ2876]騎行川藏