You live in a small town with R bidirectional roads connecting C crossings and you want to go from crossing 1 to crossing C as soon as possible. You can visit other crossings before arriving at crossing C, but it's not mandatory.

You have exactly one chance to ask your friend to repair exactly one existing road, from the time you leave crossing 1. If he repairs the i-th road for t units of time, the crossing time after that would be viai-t. It's not difficult to see that it takes vi units of time to cross that road if your friend doesn't repair it. You cannot start to cross the road when your friend is repairing it.

Input

There will be at most 25 test cases. Each test case begins with two integers C and R ( 2C100, 1R500). Each of the next R lines contains two integers xiyi ( 1xiyiC) and two positive floating-point numbers vi and ai ( 1vi45, 1ai5), indicating that there is a bidirectional road connecting crossingxi and yi, with parameters vi and ai (see above). Each pair of crossings can be connected by at most one road. The input is terminated by a test case with C = R = 0, you should not process it.

Output

For each test case, print the smallest time it takes to reach crossing C from crossing 1, rounded to 3 digits after decimal point. It's always possible to reach crossing C from crossing 1.

题目大意:有一个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呢?因为这种情况就算出现了,也肯定有比这个更优的答案,不予证明,反正我觉得是这样(咦,这个我好像前面就有提到过……)。

代码(32MS):

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std; const int MAXN = ;
const int MAXE = ;
const double EPS = 1e-; inline int sgn(double x) {
if(fabs(x) < EPS) return ;
return x > ? : -;
} double fpai(double t, double v, double a) {
//t - v * pow(a, -t)
return - log(a) * v * pow(a, - t);
} inline void update_min(double &a, const double &b) {
if(a > b) a = b;
} double mat[MAXN][MAXN];
int x[MAXE], y[MAXE];
double v[MAXE], a[MAXE];
int n, m; void floyd() {
for(int k = ; k <= n; ++k)
for(int i = ; i <= n; ++i)
for(int j = ; j <= n; ++j) update_min(mat[i][j], mat[i][k] + mat[k][j]);
} double find_t(int i, int x, int y, double l, double r) {
double L = l, R = r;
while(R - L > EPS) {
double mid = (L + R) / ;
//cout<<fpai(mid, v[i], a[i])<<endl;
if(fpai(mid, v[i], a[i]) > ) R = mid;
else L = mid;
}
if(sgn(fpai(L, v[i], a[i])) != ) return l;
return L;
} double solve() {
double t, ans = mat[][n];
for(int i = ; i < m; ++i) {
t = find_t(i, x[i], y[i], mat[][x[i]], ans);
update_min(ans, t + v[i] * pow(a[i], -t) + mat[y[i]][n]);
t = find_t(i, y[i], x[i], mat[][y[i]], ans);
update_min(ans, t + v[i] * pow(a[i], -t) + mat[x[i]][n]);
}
return ans;
} int main() {
while(scanf("%d%d", &n, &m) != EOF) {
if(n == && m == ) break;
for(int i = ; i <= n; ++i) {
for(int j = ; j <= n; ++j) mat[i][j] = 1e5;
mat[i][i] = ;
}
for(int i = ; i < m; ++i) {
int aa, bb; double cc;
scanf("%d%d%lf%lf", &aa, &bb, &cc, &a[i]);
x[i] = aa; y[i] = bb; v[i] = cc;
update_min(mat[aa][bb], cc);
update_min(mat[bb][aa], cc);
}
floyd();
printf("%.3f\n", solve());
}
}

UVA 11883 Repairing a Road(最短路径+暴力枚举)的更多相关文章

  1. HDU 3268/POJ 3835 Columbus’s bargain(最短路径+暴力枚举)(2009 Asia Ningbo Regional)

    Description On the evening of 3 August 1492, Christopher Columbus departed from Palos de la Frontera ...

  2. UVA 617 - Nonstop Travel(数论+暴力枚举)

    题目链接:617 - Nonstop Travel 题意:给定一些红绿灯.如今速度能在30-60km/h之内,求多少个速度满足一路不遇到红灯. 思路:暴力每个速度,去推断可不能够,最后注意下输出格式就 ...

  3. UVA 11059 Maximum Product【三层暴力枚举起终点】

    [题意]:乘积最大的子序列.n∈[1,10],s∈[-10,10] [代码]: #include<bits/stdc++.h> using namespace std; int a[105 ...

  4. UVA.12716 GCD XOR (暴力枚举 数论GCD)

    UVA.12716 GCD XOR (暴力枚举 数论GCD) 题意分析 题意比较简单,求[1,n]范围内的整数队a,b(a<=b)的个数,使得 gcd(a,b) = a XOR b. 前置技能 ...

  5. UVA 10012 How Big Is It?(暴力枚举)

      How Big Is It?  Ian's going to California, and he has to pack his things, including his collection ...

  6. uva 11088 暴力枚举子集/状压dp

    https://vjudge.net/problem/UVA-11088 对于每一种子集的情况暴力枚举最后一个三人小组取最大的一种情况即可,我提前把三个人的子集情况给筛出来了. 即 f[S]=MAX{ ...

  7. UVA - 11464 Even Parity 【暴力枚举】

    题意 给出一个 01 二维方阵 可以将里面的 0 改成1 但是 不能够 将 1 改成 0 然后这个方阵 会对应另外一个 方阵 另外一个方阵当中的元素 为 上 下 左 右 四个元素(如果存在)的和 要求 ...

  8. 紫书 例题 10-2 UVa 12169 (暴力枚举)

    就是暴力枚举a, b然后和题目给的数据比较就ok了. 刘汝佳这道题的讲解有点迷,书上讲有x1和a可以算出x2, 但是很明显x2 = (a * x1 +b) 没有b怎么算x2?然后我就思考了很久,最后去 ...

  9. 湖南省第6届程序大赛 Repairing a Road

    Problem G Repairing a Road You live in a small town with R bidirectional roads connecting C crossing ...

随机推荐

  1. ref、refs使用的注意事项

    ref是被用来给元素或子组件注册引用信息.引用信息将注册在父组件的 $refs 对象身上.如果在普通的DOM元素身上使用,引用指向就是DOM元素:如果用在子组件身上,引用就是指向组件实例. 当v-fo ...

  2. ETO的公开赛T3《寻星》 题解(BY 超級·考場WA怪 )

    题解 寻星 题意:给定一个有向带权图,定义从一点到另一点的某条路径长为路径上所有边权的最大值,并给定四个点编号w,t1,t2,t3. 求出一个点s,使它在到t1,t2,t3三点最短路径最大值最大或者根 ...

  3. [JSOI2008]最大数(线段树基础)

    题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: L 不超过当前数列的长度.(L > ...

  4. Cantor表

    题目描述 现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的.他是用下面这一张表来证明这一命题的: 1/1 1/2 1/3 1/4 1/5 - 2/1 2/2 2/3 2/4 - ...

  5. atan和atan2反正切计算

    typedef struct point { double x, y; }point; //给定两个点 point a(x1,y1),b(x2,y2); 使用反三角函数atan求斜率,原型如下 flo ...

  6. [tree]合并果子(哈夫曼树+优先队列)

    现在有n堆果子,第i堆有ai个果子.现在要把这些果子合并成一堆,每次合并的代价是两堆果子的总果子数.求合并所有果子的最小代价. Input 第一行包含一个整数T(T<=50),表示数据组数. 每 ...

  7. 快速玩转linux(2)

    ssh是什么   SSH:secure shell 安全外壳协议 建立在应用层基础上的安全协议 可靠, 专为远程登录会话和其他网络服务提供安全性的协议. mark 客户端服务端都基本支持全平台 服务器 ...

  8. VMware虚拟机下载与安装(内附密钥)

    VMware下载与安装 一.虚拟机的下载 1.进入VMware官网,点击左侧导航栏中的下载,再点击图中标记的Workstation Pro,如下图所示. 2.根据操作系统选择合适的产品,在这里以Win ...

  9. TcpServer 使用简介

    1.简介 1) Poco 的 TcpServer 是一个多线程的 Tcp 服务器. 服务器使用 ServerSocket(Poco 的一个用于初始化服务器的socket的类) 来接收链接.Server ...

  10. 【linux基于Postfix和Dovecot邮件系统的搭建】

    一:PostFixe和Dovecot的简单介绍 Postfix postfix是Wietse Venema在IBM的GPL协议之下开发的MTA(邮件传输代理)软件.postfix是Wietse Ven ...