• 【bzoj2876】 Noi2012—骑行川藏


     http://www.lydsy.com/JudgeOnline/problem.php?id=2876 (题目链接)

    题意

      在满足约束条件$${sum_{i=1}^ns_ik_i(v_i-v_i')^2=E}$$

      求$${minsum_{i=1}^nfrac{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}$的等高线相切→_→,既然它们的等高线相切,那么它们的梯度向量平行${ abla f~//~ abla g}$。

      梯度向量的每一维就是这个函数对应那一维的偏导数$${ abla 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}$。设${ abla f=lambda abla 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}=2lambda k_1s_1(v_1-v_1')}$$

    $${-frac{s_2}{v_2^2}=2lambda k_2s_2(v_2-v_2')}$$

    $${-frac{s_3}{v_3^2}=2lambda k_3s_3(v_3-v_3')}$$

    $${······}$$

    $${-frac{s_n}{v_n^2}=2lambda k_ns_n(v_n-v_n')}$$

    $${sum_{i=1}^nk_i(v_i-v_i')^2s_i=E}$$

      化简一下。

    $${2lambda k_1v_1^2(v_1-v_1')=-1}$$

    $${2lambda k_2v_2^2(v_2-v_2')=-1}$$

    $${2lambda k_3v_3^2(v_3-v_3')=-1}$$

    $${······}$$

    $${2lambda 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;
    }
    
  • 相关阅读:
    Python基础之 一 字典(dict)
    python基础之-字符串
    Python基础之 一列表
    Python基础之 一 补充
    python基础之-数据类型
    python之模块随笔记-sys
    python之练习-三层菜单
    Python基础之 二
    SQL Server 数据类型
    SQLServer视图
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6368825.html
Copyright © 2020-2023  润新知