• [NOI2012]骑行川藏——拉格朗日乘子法


    原题链接
    不会啊,只好现学了拉格朗日乘子法,简单记录一下

    前置芝士:拉格朗日乘子法

    要求(n)元目标函数(f(x_1,x_2,...,x_n))的极值,且有(m)个约束函数形如(h_i(x_1,x_2,...,x_n)=0)
    引入松弛变量(alpha _1-alpha _m),构造拉格朗日函数如下:

    [L(x_1,x_2,...,x_n,alpha _1,alpha _2,...,alpha _m)=f(x_1,x_2,...,x_n)+sumlimits_{i=1}^{m}alpha _ih_i(x_1,x_2,...,x_n) ]

    然后分别对(x)(a)求偏导并令偏导值为(0)($ abla $为梯度向量):

    [ abla _xL(x,alpha)=0, abla _{alpha}L(x,alpha)=0 ]

    求解上述方程组,即可求得极值点。但是解方程组的代价太大了,在做题时我们一般会通过函数的单调性二分来解
    为什么可以这样呢,考虑一下,满足条件的极值点应该是在目标函数的等高线与约束函数曲线相切的点,在这一点上有如下等式成立:

    [ abla _xf(x)=a abla _xh(x) ]

    而拉格朗日函数求导之后和上式本质相同,因此它能求得最值
    还有广义拉格朗日乘子法是适用于有不等式约束的情况

    题解

    首先我们把目标函数和约束函数都找出来

    目标函数$f(x)=sumlimits_{i=1}^{n}frac{s_i}{v_i}$
    约束函数$g(x)=sumlimits_{i=1}^{n}k_is_i(v_i-v'_i)^2-E_U$
    那么拉格朗日函数为 $$L(x,alpha)=f(x)+alpha g(x)=-alpha E_U+sumlimits_{i=1}^{n}frac{s_i}{v_i}+alpha k_is_i(v_i-v'_i)^2$$ 求出$v_i$关于$L$的偏导并将其设置为$0$ $$frac{partial L(v,alpha)}{partial v_i}=-frac{s_i}{v_i^2}+2alpha s_ik_i(v_i-v'_i)=0$$ $$Rightarrow alpha=frac{1}{2k_iv_i^2(v_i-v'_i)}$$ 经过简单讨论,可以得出$alpha$随$v$单调递减,$g$随$v$单调递增,所以$g$随$alpha$单调递减 于是我们可以先二分$alpha$,然后再二分解出$v$,复杂度$O(nlog^2n)$ 附代码: ```cpp #include

    using namespace std;

    define N 10000

    const double eps = 1e-13, INF = 1e5;
    int n;
    double Eu, s[N + 5], k[N + 5], v0[N + 5], v[N + 5];

    bool check(double lamda) {
    for (int i = 1; i <= n; ++i) {
    double tar = 1 / (2 * k[i] * lamda), l = max(v0[i], 0.0), r = INF, mid;
    while(r - l >= eps) {
    mid = (l + r) / 2;
    if (mid * mid * (mid - v0[i]) > tar) r = mid;
    else l = mid;
    }
    v[i] = mid;
    }
    double E = 0;
    for (int i = 1; i <= n; ++i)
    E += k[i] * s[i] * pow(v[i] - v0[i], 2);
    return E <= Eu;
    }

    int main() {
    cin >> n >> Eu;
    for (int i = 1; i <= n; ++i)
    cin >> s[i] >> k[i] >> v0[i];
    double l = 0, r = INF, mid;
    while (r - l >= eps) {
    mid = (l + r) / 2;
    if (check(mid)) r = mid;
    else l = mid;
    }
    double ans = 0;
    for (int i = 1; i <= n; ++i)
    ans += s[i] / v[i];
    cout << setiosflags(ios::fixed) << setprecision(10);
    cout << ans << endl;
    return 0;
    }

  • 相关阅读:
    Android基础
    Android基础
    Java小项目——多线程弹球
    Java小项目——抽奖系统
    Java小项目——五子棋
    Java小项目——画板
    Java基础——swing登录界面
    Java基础——类的继承
    实验室资料说明
    20180919 百信、百度面试
  • 原文地址:https://www.cnblogs.com/dummyummy/p/11125711.html
Copyright © 2020-2023  润新知