http://www.lydsy.com/JudgeOnline/problem.php?id=2876 (題目鏈接)
題意
在滿足約束條件$${\sum_{i=1}^ns_ik_i(v_i-v_i')^2=E}$$
求$${min\sum_{i=1}^n\frac{s_i}{v_i}}$$
Solution
像這種形式的存在一個多元函數${g(v_1,v_2,v_3,······,v_n)=E}$的約束,求解多元函數${f(v_1,v_2,v_3,······,v_n)}$的最值,我們使用拉格朗日乘子法。
在解這道題之前,我們要知道什么是偏導數,梯度向量,等高線等等一系列的東西。當然我是不知道的,只能靠自己YY了。如果這些你都知道了,那么你應該就會知道到當${f}$取到最值時,${f}$和${g}$的等高線相切→_→,既然它們的等高線相切,那么它們的梯度向量平行${\nabla f~//~\nabla g}$。
梯度向量的每一維就是這個函數對應那一維的偏導數$${\nabla f=(\frac{\partial f}{\partial v_1},\frac{\partial f}{\partial v_2},\frac{\partial f}{\partial v_3}······,\frac{\partial f}{\partial v_n})}$$
因為${f}$和${g}$的梯度向量平行,我們只要知道它們在哪一個點平行,我們就解出了${v_1···v_n}$。設${\nabla f=\lambda \nabla g}$,我們可以列出${n+1}$個等式。
$${\frac{\partial f}{\partial v_1}=\lambda \frac{\partial g}{\partial v_1}}$$
$${\frac{\partial f}{\partial v_2}=\lambda \frac{\partial g}{\partial v_2}}$$
$${\frac{\partial f}{\partial v_3}=\lambda \frac{\partial g}{\partial v_3}}$$
$${······}$$
$${\frac{\partial f}{\partial v_n}=\lambda \frac{\partial g}{\partial v_n}}$$
$${g(v_1,v_2,v_3,······,v_n)=E}$$
求出偏導數代入。
$${-\frac{s_1}{v_1^2}=2\lambda k_1s_1(v_1-v_1')}$$
$${-\frac{s_2}{v_2^2}=2\lambda k_2s_2(v_2-v_2')}$$
$${-\frac{s_3}{v_3^2}=2\lambda k_3s_3(v_3-v_3')}$$
$${······}$$
$${-\frac{s_n}{v_n^2}=2\lambda k_ns_n(v_n-v_n')}$$
$${\sum_{i=1}^nk_i(v_i-v_i')^2s_i=E}$$
化簡一下。
$${2\lambda k_1v_1^2(v_1-v_1')=-1}$$
$${2\lambda k_2v_2^2(v_2-v_2')=-1}$$
$${2\lambda k_3v_3^2(v_3-v_3')=-1}$$
$${······}$$
$${2\lambda k_nv_n^2(v_n-v_n')=-1}$$
$${\sum_{i=1}^nk_i(v_i-v_i')^2s_i=E}$$
考慮${v_i}$的范圍。如果對應的${v_i'<0}$,吹的是逆風,那么${v_i>0}$;如果${v_i'>0}$,吹的是順風,那么讓${v_i=v_i'}$一定優於${v_i<v_i'}$,於是我們得到了${v_i}$的下界。${v_i}$的上界就是把所有的能量全部用在這一段路上所能達到的最大速度(然而程序里面上界不能這樣寫,數據有幾個點存在${s_i=0}$的情況,smg嘛→_→)。
知道了${v_i}$的下界,那么顯然${k_iv_i^2(v_i-v_i')>0}$,所以${\lambda<0}$。所以${v_i}$隨着${\lambda}$的增大而增大,而${\sum_{i=1}^nk_i(v_i-v_i')^2s_i}$隨着${v_i}$的增大而增大。所以我們二分${\lambda}$,進而二分求解${v_i}$,把${v_i}$代入函數${g}$,與${E}$比較比較大小來判斷${\lambda}$的范圍是應該往上還是往下。
細節
公式不要寫錯,精度把握好。
代碼
// bzoj2876
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 2147483640
#define eps 1e-13
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int maxn=100010;
long double s[maxn],k[maxn],v[maxn],vp[maxn],maxv[maxn],Eu;
int n;
long double check(long double lambda) {
long double tmp=0;
for (int i=1;i<=n;i++) {
long double l=max((long double)0.0,vp[i]),r=maxv[i];v[i]=l;
while (l<=r) {
long double mid=(l+r)/2;
if (2*lambda*mid*mid*k[i]*(mid-vp[i])>=-1) l=mid+eps,v[i]=mid;
else r=mid-eps;
}
tmp+=k[i]*(v[i]-vp[i])*(v[i]-vp[i])*s[i];
}
return tmp<=Eu;
}
int main() {
scanf("%d%LF",&n,&Eu);
for (int i=1;i<=n;i++) {
scanf("%Lf%Lf%Lf",&s[i],&k[i],&vp[i]);
//maxv[i]=vp[i]+sqrt(Eu/k[i]/s[i]);
maxv[i]=inf;
}
long double l=-inf,r=0,lambda;
while (l<=r) {
long double mid=(l+r)/2;
if (check(mid)) l=mid+eps,lambda=mid;
else r=mid-eps;
}
check(lambda);
long double ans=0;
for (int i=1;i<=n;i++) ans+=s[i]/v[i];
printf("%.6Lf",ans);
return 0;
}
