1. 程式人生 > >bzoj2876 [NOI2012]騎行川藏(拉格朗日乘數法)

bzoj2876 [NOI2012]騎行川藏(拉格朗日乘數法)

輸入輸出格式 int 至少 lin 答案 就是 math spa space

題目描述

蛋蛋非常熱衷於挑戰自我,今年暑假他準備沿川藏線騎著自行車從成都前往拉薩。川藏線的沿途有著非常美麗的風景,但在這一路上也有著很多的艱難險阻,路況變化多端,而蛋蛋的體力十分有限,因此在每天的騎行前設定好目的地、同時合理分配好自己的體力是一件非常重要的事情。

由於蛋蛋裝備了一輛非常好的自行車,因此在騎行過程中可以認為他僅在克服風阻做功(不受自行車本身摩擦力以及自行車與地面的摩擦力影響)。某一天他打算騎\(N\)段路,每一段內的路況可視為相同:對於第\(i\)段路,我們給出有關這段路況的3個參數 \(s_i ,k_i ,v_i'\) ,其中 \(s_i\) 表示這段路的長度, \(k_i\)

表示這段路的風阻系數, \(v_i'\) 表示這段路上的風速(表示在這段路上他遇到了順風,反之則意味著他將受逆風影響)。若某一時刻在這段路上騎車速度為\(v\),則他受到的風阻大小為 \(F = k_i ( v - v_i' )^2\)(這樣若在長度為\(s\)的路程內保持騎行速度\(v\)不變,則他消耗能量(做功)\(E = k_i ( v - vi' )^2 s\)

設蛋蛋在這天開始時的體能值是 \(Eu\) ,請幫助他設計一種行車方案,使他在有限的體力內用最短的時間到達目的地。請告訴他最短的時間\(T\)是多少。

輸入輸出格式

輸入格式:
第一行包含一個正整數\(N\)

和一個實數\(Eu\),分別表示路段的數量以及蛋蛋的體能值。
接下來\(N\)行分別描述\(N\)個路段,每行有3個實數 \(s_i , k_i , v_i'\) ,分別表示第 \(i\) 段路的長度,風阻系數以及風速。
輸出格式:
輸出一個實數\(T\),表示蛋蛋到達目的地消耗的最短時間,要求至少保留到小數點後\(6\)位。

輸入輸出樣例

輸入樣例#1:

3 10000
10000 10 5
20000 15 8
50000 5 6

輸出樣例#1:

12531.34496464

說明

【數據規模與約定】
對於10%的數據,\(N=1\)

對於40%的數據,\(N<=2\)

對於60%的數據,\(N<=100\)

對於80%的數據,\(N<=1000\)

對於所有數據,\(N \leq10000\)\(0 \leq Eu \leq 10^8,0 < s_i \leq 100000,0 < k_i \leq 1,-100 < v_i' < 100\)。數據保證最終的答案不會超過\(10^5\)
【提示】
必然存在一種最優的體力方案滿足:蛋蛋在每段路上都采用勻速騎行的方式。

題解

先講一講拉格朗日乘數法:
拉格朗日乘數法是用來解決多元函數的最優值問題(最大、最小)
一般形式為:函數\(f(x_1,x_2,x_3..x_n)\)滿足限制\(g_i(x_1,x_2,x_3...x_n)=0,(i\in 1,2,3....m)\)
解法:定義\(h(x_1,x_2,x_3...x_n,\lambda_1,\lambda_2,\lambda_3...\lambda_m)=f(x_1,x_2,x_3...x_n)+\Sigma_{i=1}^m\lambda_ig_i(x_1,x_2,x_3...x_n)\)
函數\(h\)的極值就是函數\(f\)的最優值
\(h\)極值用導數求

再回到這道題,只需要滿足一種限制:\(g(x)=\Sigma_{i=1}^n s_i\ast k_i(x_i-v_i')^2\leq Eu\),並且,當\(g(x)=Eu\)時最優;
於是就有\(g(x)\)函數:\(g(x)=\Sigma_{i=1}^n s_i\ast k_i(x_i-v_i')^2-Eu=0\)
\(f(x)\)函數為:\(f(x)=\Sigma_{i=1}^n \frac {s_i}{x_i}\)\(x_i\)為每段路的騎行速度

\(h(x)=\Sigma_{i=1}^n \frac{s_i}{x_i}+\lambda\ast\Sigma s_i\ast k_i(x_i-v_i')^2-Eu\)
將它求導:\(h'(x)=-\Sigma_{i=1}^n \frac{s_i}{x_i^2}+2\ast\lambda\ast\Sigma_{i=1}^n s_i\ast k_i(x_i-v_i')\)

二分求\(h'(x)=0\)時的\(x\)
二分時每一個\(x_i\)也是二分求

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<set>
#include<bitset>
#include<vector>
#include<cstdlib>
#define QAQ int
#define TAT long long
#define OwO bool
#define ORZ double
#define F(i,j,n) for(QAQ i=j;i<=n;++i)
#define E(i,j,n) for(QAQ i=j;i>=n;--i)
#define MES(i,j) memset(i,j,sizeof(i))
#define MEC(i,j) memcpy(i,j,sizeof(j))
using namespace std;
const QAQ N=10005;
QAQ n;
ORZ m;
struct data{
    ORZ s,k,v;
}a[N];
ORZ l,r,ans,v[N];
OwO pd(ORZ lmd){
    F(i,1,n){
        ORZ l=a[i].v,r=1000000,ans=0;
        F(j,1,100){
            ORZ mid=(l+r)/2.0;
            if(2*lmd*a[i].k*mid*mid*(mid-a[i].v)<=1.0) l=mid,ans=mid;
            else r=mid;
        }
        v[i]=ans;
    }
    ORZ ans=0;
    F(i,1,n) ans+=a[i].k*(v[i]-a[i].v)*(v[i]-a[i].v)*a[i].s;
    return ans>=m;
}
QAQ main(){
    scanf("%d%lf",&n,&m);
    F(i,1,n) scanf("%lf%lf%lf",&a[i].s,&a[i].k,&a[i].v);
    l=0;r=10000000;
    F(i,1,100){
        ORZ mid=(l+r)/2.0;
        if(pd(mid)) l=mid;
        else r=mid;
    }
    F(i,1,n) ans+=a[i].s/v[i];
    printf("%.6lf\n",ans);
    return 0;
}

bzoj2876 [NOI2012]騎行川藏(拉格朗日乘數法)