【拉格朗日乘数法】bzoj2876: [Noi2012]骑行川藏

来源:互联网 发布:用powermill编程怎么样 编辑:程序博客网 时间:2024/04/28 00:40

题目链接

普及一下拉格朗日乘数法。
知识链接

问题:求f(a,b,c,...)的极值,其中g(a,b,c,...)=0
即一个多元式在某一限制下的极值。

f+λg=0
于是有:

f(a)+λg(a)=0

f(b)+λg(b)=0

......

g(a,b,c,...)=0

n个方程,n个未知数,理论上是可以求出a,b,c,...的具体值的。

对于此题,拉格朗日的方程为:

siv2i+2λkisi(vivi)=0

化简一下:
2λkiv2i(vivi)=1

二分一下λ,然后求出每一个方程的解检验一下即可。

#include <iostream>#include <cstdio>#include <cmath>#define eps 1e-12#define MAXN 10005using namespace std;int n;double s[MAXN], k[MAXN], v[MAXN], E, vv[MAXN], vmax[MAXN];double cal(double num){    double sum=0, l, r, mid, a, b;    for(int i=1;i<=n;++i)    {        l=v[i], r=vmax[i];        a=2*num*k[i], b=-a*v[i];        while(l+eps<r)        {            mid=(l+r)/2;            if(a*mid*mid*mid+b*mid*mid-1>0)r=mid;            else l=mid;        }        vv[i]=(l+r)/2;        sum+=s[i]*k[i]*(vv[i]-v[i])*(vv[i]-v[i]);    }    return sum;}int main(){    scanf("%d%lf",&n,&E);    for(int i=1;i<=n;++i)    {        scanf("%lf%lf%lf",&s[i],&k[i],&v[i]);        vmax[i]=s[i]>0?v[i]+sqrt(E/s[i]/k[i]):0;    }    double l=0, r=1e4, mid;    while(r-l>eps)    {        mid=(l+r)/2;        if(cal(mid)<E)r=mid;        else l=mid;    }    double ans=0;    for(int i=1;i<=n;++i)ans+=s[i]/vv[i];    printf("%.10f\n",ans);    return 0;}
0 0
原创粉丝点击