Repairing a Road(floyd演算法)

feng_zhiyu發表於2017-08-12

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 (2<=C<=100, 1<=R<=500). Each of the next R lines contains two integers xi, yi (1<=xi, yi<=C) and two positive floating-point numbers vi and ai (1<=vi<=20,1<=ai<=5), indicating that there is a bidirectional road connecting crossing xi 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.
Sample Input
3 2
1 2 1.5 1.8
2 3 2.0 1.5
2 1
1 2 2.0 1.8
0 0
Sample Output
2.589
1.976

題意:給C個頂點(2<=C<=100), R(1<=R<=500)條邊, 的無向圖. 其中每條邊上有起點, 終點, 時間權值vi, 和另一引數ai。如果這道路總共修了t時間,那麼這條路的花費就會變成 vi * ai ^ (-t)。求從1到C的最小時間

之前課程設計的時候寫了這題,當時對最短路演算法沒有現在的深刻了解,看這道題的時候比較懵,看了別人寫的題解,然後覺得清晰了不少,自己寫寫,整理整理思路。。

分析:這題要求最小時間,那麼是最短路問題。 然後求的是1到C的距離,C的範圍不大,採用floyd演算法,時間複雜度約為O(C^3).基本就是floyd演算法的時間。
題中修一條路如果用了t時間,這條路的話費就會變成vi*ai^(-t), 單調遞減函式,可以從這裡看到當a!=1時,t越大,所用時間越少,但既然是求最少時間,為什麼修的越久越好? 噗,這個就是題意部分的了, 路可以任意時候開始修,但是修路期間不能通過這條路。
那麼這時候求出修路的最長時間+經過某條路的時間+到達C點的最短時間,取最小即為所求

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
#define mem(a,n) memset(a,n,sizeof(a))
const double INF=0x3f3f3f3f+1.0;
const double eps=1e-6;
typedef long long LL;
const int N=105;
double d[N][N];
int C,R;
struct Edge
{
    int u,to;
    double v,a;
};
queue<Edge>que;
////求兩點間最短距離
void floyd()
{
    for(int k=1; k<=C; k++)
        for(int i=1; i<=C; i++)
            for(int j=1; j<=C; j++)
            {
                if(d[i][j]>d[i][k]+d[k][j])
                    d[i][j]=d[i][k]+d[k][j];
            }
}
double solve(double sum)
{
    Edge tou;
    while(!que.empty())
    {
        tou=que.front();
        que.pop();
        double t=0;
        if(abs(tou.a-1)>eps) ///若a不為1時,a為1時會發生除0錯誤
        {    ///根據y =t+v*a^(-t) => y'=1+(-v*a^(-t)ln(a))=0
            /// => a^t=v*ln(a) => t=log(a,v*ln(a))
            /// => t=ln(v*ln(a))/ln(a)
            t=log(tou.v*log(tou.a))/log(tou.a);
        }
        t=max(t,d[1][tou.u]);///求修 1->tou.u 這條路的最長時間
        sum=min(sum,t+tou.v*pow(tou.a,-t)+d[tou.to][C]);///sum為總的最短時間
    }
    return sum;
}
int main()
{
    while(~scanf("%d%d",&C,&R)&&C&&R)
    {
        while(!que.empty())///清空佇列
            que.pop();
         ///初始化  最短時間
        for(int i=1; i<=C; i++)
            for(int j=1; j<=C; j++)
            {
                if(i==j) d[i][i]=0;
                else d[i][j]=INF;
            }
        Edge E;
        for(int i=0; i<R; i++)
        {
            scanf("%d%d%lf%lf",&E.u,&E.to,&E.v,&E.a);
            int t1=E.u,t2=E.to;
            d[t1][t2]=E.v;
            d[t2][t1]=E.v;
            que.push(E);
            int tmp=E.u;
            E.u=E.to;
            E.to=tmp;
            que.push(E);
        }
        floyd();
        double ans=d[1][C];
        printf("%.3lf\n",solve(ans));
    }
    return 0;
}

相關文章