[NOI2012]骑行川藏 题解

传送门

题意:

给出 u 1 , u 2 , ⋯   , u n , k 1 , k 2 , ⋯   , k n , s 1 , s 2 , ⋯   , s n u_1,u_2,\cdots,u_n,k_1,k_2,\cdots,k_n,s_1,s_2,\cdots,s_n u1,u2,,un,k1,k2,,kn,s1,s2,,sn

最小化函数 T = ∑ i = 1 n s i v i T=\sum\limits_{i=1}^n\frac{s_i}{v_i} T=i=1nvisi的值,并满足条件 φ = ∑ i = 1 n k i s i ( v i − u i ) 2 − E U = 0 \varphi=\sum\limits_{i=1}^nk_is_i(v_i-u_i)^2-E_U=0 φ=i=1nkisi(viui)2EU=0

原本题目描述是说 φ ≤ 0 \varphi\leq0 φ0,但一个简单的贪心思想就是能量消耗越多时间肯定越少,所以直接令 φ = 0 \varphi=0 φ=0即可。

题解:

这是裸的条件极值,考虑拉格朗日乘数法。

首先介绍偏导数的概念:对于多元函数 y = f ( x 1 , x 2 , ⋯   , x n ) y=f(x_1,x_2,\cdots,x_n) y=f(x1,x2,,xn),可以选定其中一个 x i x_i xi视为变量,将其它的 x j ( j ≠ i ) x_j(j\neq i) xj(j̸=i)都视为常数,将 y y y关于 x i x_i xi求导,称为偏导函数,记作 ∂ f ∂ x i \frac{\partial f}{\partial x_i} xif f x i ( x 1 , x 2 , ⋯   , x n ) f_{x_i}(x_1,x_2,\cdots,x_n) fxi(x1,x2,,xn)

拉格朗日乘数法:求函数 f ( x 1 , x 2 , ⋯   , x n ) f(x_1,x_2,\cdots,x_n) f(x1,x2,,xn)在满足条件 φ ( x 1 , x 2 , ⋯   , x n ) = 0 \varphi(x_1,x_2,\cdots,x_n)=0 φ(x1,x2,,xn)=0的情况下的可能极值点,可以构造函数 L = f ( x 1 , x 2 , ⋯   , x n ) + λ φ ( x 1 , x 2 , ⋯   , x n ) L=f(x_1,x_2,\cdots,x_n)+\lambda\varphi(x_1,x_2,\cdots,x_n) L=f(x1,x2,,xn)+λφ(x1,x2,,xn),然后联立方程组:

{ ∂ L ∂ x 1 = 0 ∂ L ∂ x 2 = 0 ⋯ ∂ L ∂ x n = 0 φ ( x 1 , x 2 , ⋯   , x n ) = 0 \begin{cases} \frac{\partial L}{\partial x_1}=0\\ \frac{\partial L}{\partial x_2}=0\\ \cdots\\ \frac{\partial L}{\partial x_n}=0\\ \varphi(x_1,x_2,\cdots,x_n)=0 \end{cases} x1L=0x2L=0xnL=0φ(x1,x2,,xn)=0

这里一共有 ( n + 1 ) (n+1) (n+1)个方程,并且算上 λ \lambda λ一共有 ( n + 1 ) (n+1) (n+1)个未知数,可以解出若干组解,它们就是函数 f f f在满足条件 φ = 0 \varphi=0 φ=0的情况下的所有可能极值点。

对于本题而言,设 L = T + λ φ = ∑ i = 1 n s i v i + λ ( ∑ i = 1 n k i s i ( v i − u i ) 2 − E U ) L=T+\lambda\varphi=\sum\limits_{i=1}^n\frac{s_i}{v_i}+\lambda\left(\sum\limits_{i=1}^nk_is_i(v_i-u_i)^2-E_U\right) L=T+λφ=i=1nvisi+λ(i=1nkisi(viui)2EU)

那么 L L L关于 v i v_i vi的偏导数为

∂ L ∂ x i = − s i v i 2 + 2 λ k i s i ( v i − u i ) \frac{\partial L}{\partial x_i}=-\frac{s_i}{v_i^2}+2\lambda k_is_i(v_i-u_i) xiL=vi2si+2λkisi(viui)

令上式为 0 0 0,稍作变形得到方程

2 λ k i ( v i − u i ) v i 2 − 1 = 0 2\lambda k_i(v_i-u_i)v_i^2-1=0 2λki(viui)vi21=0

注意到 v i v_i vi必须大于等于 u i u_i ui。因为如果 u i ≤ 0 u_i\leq0 ui0 v i &gt; 0 &gt; u i v_i&gt;0&gt;u_i vi>0>ui显然;而当 u i &gt; 0 u_i&gt;0 ui>0时, v i &lt; u i v_i&lt;u_i vi<ui意味着你在反向使劲。

并且这个方程要有解必须 λ &gt; 0 \lambda&gt;0 λ>0,因为 λ ≤ 0 \lambda\leq0 λ0时左边必然是负的。

考虑函数 g ( x ) = 2 λ k i ( x − u i ) x 2 − 1 g(x)=2\lambda k_i(x-u_i)x^2-1 g(x)=2λki(xui)x21,求导 g ′ ( x ) = 6 λ k i x 2 − 4 λ k i u i x g&#x27;(x)=6\lambda k_ix^2-4\lambda k_iu_ix g(x)=6λkix24λkiuix。当 λ &gt; 0 , x ≥ u i \lambda&gt;0,x\geq u_i λ>0,xui g ′ ( x ) &gt; 0 g&#x27;(x)&gt;0 g(x)>0 g ( x ) g(x) g(x)单调递增。

所以对于给定的 λ \lambda λ,我们可以二分来求解方程 g ( v i ) = 0 g(v_i)=0 g(vi)=0的解。

可是 λ \lambda λ并没有给定怎么办?没有关系。从上面这个方程我们看出, λ \lambda λ越大, v i v_i vi越小,相应的 φ = ∑ i = 1 n k i s i ( v i − u i ) 2 − E U \varphi=\sum\limits_{i=1}^nk_is_i(v_i-u_i)^2-E_U φ=i=1nkisi(viui)2EU就越小。现在我们要使得 φ = 0 \varphi=0 φ=0,可以二分 λ \lambda λ

#include <cstdio>
#include <climits>
#include <cmath>
#include <algorithm>

const int maxn = 1e4 + 7;
const double eps = 1e-12, inf = 1e5;

int n;
double eu, s[maxn], k[maxn], u[maxn], v[maxn];

inline bool check(double lambda) {
    double e = 0;
    for (int i = 1; i <= n; ++i) {
        double left = std::max(u[i], 0.0), right = inf;
        while (right - left >= eps) {
            double mid = (left + right) / 2.0;
            if (2 * lambda * k[i] * (mid - u[i]) * mid * mid >= 1)
                right = mid;
            else left = mid;
        }
        v[i] = left;
        e += k[i] * s[i] * (v[i] - u[i]) * (v[i] - u[i]);
    }
    return e >= eu;
}

int main() {
    scanf("%d%lf", &n, &eu);
    for (int i = 1; i <= n; ++i) scanf("%lf%lf%lf", s + i, k + i, u + i);
    double left = 0, right = inf;
    while (right - left >= eps) {
        double mid = (left + right) / 2.0;
        if (check(mid)) left = mid;
        else right = mid;
    }
    double t = 0;
    for (int i = 1; i <= n; ++i) t += s[i] / v[i];
    printf("%.8lf\n", t);
    return 0;
}

求解那个偏导数 = 0 =0 =0的方程可能还可以用牛顿迭代法来提高效率,可是我好像WA了…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值