zoukankan      html  css  js  c++  java
  • G

    题目:Repairing a Road

    题目大意:有一个C个点R条无向边(这俩字母好别扭……),每条边有一个花费vi和一个ai(浮点数)。现在我们有一个人,可以花费 t(你爱多大就多大)的时间该修一条道路,什么时候开始修随你,修到什么时候随你,不过修的时候此路不通。如果总共修了t时间,那么这条路的花费就会变成 vi * ai ^ (-t)。我们要从1走到C,求最小花费(花费就是时间)。

    思路:首先要修路,肯定是从一开始就开始修,因为看出修得越久该路的花费就越少(不要告诉我你看不出那个是单调递减的函数),所以修的 时间越长越好,而且,总不会说,我从这条路走过去了,然后你再开始修,修了一段时间,我再走一次,因为这都是无向边,根据最短路的性质一条边我们是肯定不 会走两遍的,就算第二遍时间减少了也好。

    其次,如果我们要修(x, y)这条边,我们要从x走到y,我们可能要先在x等一段时间,然后再从x走到y。我们等了一段时间再过去,路的花费也就减少了,可能要比我们直接走过去花的时间要更少。(样例真是业界良心……)

    然后怎么办了,我们这里只有500条边,所以可以枚举每一条边,然后我们通过这一条边(可能从x到y也可能从y到x因为边是双向的,实 际上大概有方法判定不过我懒……)。比如经过一条边(x, y)的最短路径就是dis(1, x) + (t - dis(1, x)) + vi * ai ^ (-t) + dis(y, C),第二个是可能站在x等待的时间,从最优的角度考虑t肯定是不会小于dis(1, x)。

    其中dis(a, b)代表从a走到b所需要的最小花费。我们可以从1开始做单源最短路径,再从C开始做单源最短路径。嗯?C只有100?果断floyd,这太好写了。

    设f(t) = dis(1, x) + (t - dis(1, x)) + vi * ai ^ (-t) + dis(y, C),怎么令这个f(t)最大呢?哦,不对,最小……首先我们可以观察一下这个函数,咳咳,观察不出来,求导吧……f'(t) = 1 - ln(ai) * vi * ai ^ (-t),求这个函数的零点,很好,这是一个单调的函数,果断二分求解0点。下界很简单,就是dis(1, x),那么上界呢?上界我们可以定为当前答案ans(初始化为dis(1, C)),因为如果修的时间比这个还多就没有意义了。问题是,这个是区间求零点,很可能没有零点,怎么办?如果零点在dis(1, x)的左边,那么t取dis(1, x)就好。如果在上界右边呢?这个我没分析也直接取了dis(1, x),因为我觉得应该不会出现要取上界右边这么坑爹的情况,反正就算取了它也不会是答案了,随便怎么样都无所谓了。

    其实这上面的做法有个BUG,就是我可能从1走到x的时候,经过了边(x, y),然后其实修理(x, y)的时候我们并不能走(x, y)。但是为什么会AC呢?因为这种情况就算出现了,也肯定有比这个更优的答案,不予证明,反正我觉得是这样(咦,这个我好像前面就有提到过……)。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <iostream>
     5 #include <cmath>
     6 using namespace std;
     7 
     8 const int MAXN = 110;
     9 const int MAXE = 1010;
    10 const double EPS = 1e-6;
    11 
    12 inline int sgn(double x) {
    13     if(fabs(x) < EPS) return 0;
    14     return x > 0 ? 1 : -1;
    15 }
    16 
    17 double fpai(double t, double v, double a) {
    18     //t - v * pow(a, -t)
    19     return 1 - log(a) * v * pow(a, - t);
    20 }
    21 
    22 inline void update_min(double &a, const double &b) {
    23     if(a > b) a = b;
    24 }
    25 
    26 double mat[MAXN][MAXN];
    27 int x[MAXE], y[MAXE];
    28 double v[MAXE], a[MAXE];
    29 int n, m;
    30 
    31 void floyd() {
    32     for(int k = 1; k <= n; ++k)
    33         for(int i = 1; i <= n; ++i)
    34             for(int j = 1; j <= n; ++j) update_min(mat[i][j], mat[i][k] + mat[k][j]);
    35 }
    36 
    37 double find_t(int i, int x, int y, double l, double r) {
    38     double L = l, R = r;
    39     while(R - L > EPS) {
    40         double mid = (L + R) / 2;
    41         //cout<<fpai(mid, v[i], a[i])<<endl;
    42         if(fpai(mid, v[i], a[i]) > 0) R = mid;
    43         else L = mid;
    44     }
    45     if(sgn(fpai(L, v[i], a[i])) != 0) return l;
    46     return L;
    47 }
    48 
    49 double solve() {
    50     double t, ans = mat[1][n];
    51     for(int i = 0; i < m; ++i) {
    52         t = find_t(i, x[i], y[i], mat[1][x[i]], ans);
    53         update_min(ans, t + v[i] * pow(a[i], -t) + mat[y[i]][n]);
    54         t = find_t(i, y[i], x[i], mat[1][y[i]], ans);
    55         update_min(ans, t + v[i] * pow(a[i], -t) + mat[x[i]][n]);
    56     }
    57     return ans;
    58 }
    59 
    60 int main() {
    61     while(scanf("%d%d", &n, &m) != EOF) {
    62         if(n == 0 && m == 0) break;
    63         for(int i = 1; i <= n; ++i) {
    64             for(int j = 1; j <= n; ++j) mat[i][j] = 1e5;
    65             mat[i][i] = 0;
    66         }
    67         for(int i = 0; i < m; ++i) {
    68             int aa, bb; double cc;
    69             scanf("%d%d%lf%lf", &aa, &bb, &cc, &a[i]);
    70             x[i] = aa; y[i] = bb; v[i] = cc;
    71             update_min(mat[aa][bb], cc);
    72             update_min(mat[bb][aa], cc);
    73         }
    74         floyd();
    75         printf("%.3f
    ", solve());
    76     }
    77 }
  • 相关阅读:
    博客园装修 js动态背景效果 一个随鼠标变换的动态线条
    转载:jQuery在线引用地址
    记录下es6的基础笔记
    js、css 、html平时的一些小笔记
    【机器学*】k*邻算法-01
    【Leetcode】718. 最长重复子数组
    【嵌入式】嵌入式系统开发与应用第二版课后答案第三章(田泽)
    【嵌入式】嵌入式系统开发与应用第二版课后答案第二章(田泽)
    【嵌入式】嵌入式系统开发与应用第二版课后答案第一章(田泽)
    【C++】关键字回忆leetcode题解
  • 原文地址:https://www.cnblogs.com/scnuacm/p/3280331.html
Copyright © 2011-2022 走看看