问题 Q: 2010省赛题:Repairing a Road

问题 Q: 2010省赛题:Repairing a Road

题目描述
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.
输入
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.
输出
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.
样例输入
3 2
1 2 1.5 1.8
2 3 2.0 1.5
2 1
1 2 2.0 1.8
0 0
样例输出
2.589
1.976
提示

//
第二组数据说明, 是可以等待维修的。

这题昨天,折腾了一天,到晚上才出了这题,AC了之后并不觉得是很难,但没出时一直觉得烦躁,测试数据也不好写。

早上做这题时,没认真思考,直接用枚举选择每一条边来维修,并且用Dijs算法不断增加边,同时每次选取一个结点后,更新该边的v‘ = v * a^(-time) ,time为从开始走的时间,由图论知识易知肯定不会多次访问一个结点,所以选择这个结点并走这个边后,就应当停止更新
然后超时了。。。
我又想到在走到优化边前,之前的过程是一定的(因为Dijr是贪心过程),就把不优化边的Dijr过程和各个结点的信息记录,然而还是TLE
到了中午时,龙哥提醒走过优化边后的过程一定,我才开始真正意义上思考这题(之前只是无脑想AC)

这里写图片描述

/*
    如上图,在1~X前,是贪心过程,所以是一定是固定的,那么为什么Y~n不受影响?
    因为X->Y边是独立于Y~n的,自然不会影响
    当然,我有一个很好的解释,如果你找的是n->1的最短路,那么n->Y自然最短
        而这题是无向图,所以T[n->Y] = T [Y->n]
    那么问题就非常容易了
    可以得到计算公式 T[1->n]min  = min { T[1-X] + t[X->Y]+T[Y ->n]}
    T[1-X],T[Y ->n]怎么求?  --> Floyd 简洁明了易实现
    而t[X - >Y]= t<等待> +v * a^(t<等待> + time)
    //time是已经走的时间T【1-X】是常量,t<等待>是变量,记t[X - >Y]为 t
    对t<等待>求导 t'= 1 - lna * v * a^(t<等待> + time)
    而再次求导会发现 t'' =  v * a^(t<等待> + time)*(lna)^2 >=0 说明t'至多一个零点
    t' = 0 时 ,t取得最小值,此时t<等待>可能小于0,这就说明了到该点就走 t<等待>应该为0
    否则还是老老实实等t<等待>秒更快
    不难解得t = log<a>(v*ln(a)) - time;//是底数<a>
    换底公式得t = log(v*log(a))/log(a) - time;
    再有 t = t>0?t:0 
    那么 t(X ->Y) = t + v*a^(-(t+time));
*/
//Floyd AC:
#include <bits/stdc++.h>
using namespace std;
#define MAXN 400
#define INF 5000
typedef struct {double v,a;}UIO;
UIO Cross[2*MAXN][2*MAXN]; //!X,Y,Uio 题目说了只会有一条边
bool Used[2*MAXN][2*MAXN];//!Cross检索表
double Dis[2*MAXN][2*MAXN];
void Init(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) Cross[i][j].v = i==j? 0:INF;
}
void Copy(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) Dis[i][j] = Cross[i][j].v;
}
void Print(int n)
{
    for(int i=1;i<=n;i++,putchar('\n'))
        for(int j=1;j<=n;j++) printf("%5.3lf ",Cross[i][j].v);
}
double Cal(double v,double a,double time)//!过该边时最优时间
{
    double t = log(v*log(a))/log(a) - time;
    t = t>0?t:0;
    return t + pow(a,-(t+time))*v;
}
void Handle(int n,int m)
{
    Init(n);
    for(int i=1;i<=m;i++)
    {
        int X,Y;double V,A;
        scanf("%d %d %lf %lf",&X,&Y,&V,&A);
        Cross[Y][X].v = Cross[X][Y].v = V;
        Cross[Y][X].a = Cross[X][Y].a = A;
    }
}
void Flyod(int n)
{
    Copy(n);
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            if(Dis[i][k] < INF && Dis[k][j] < INF) Dis[i][j] = min(Dis[i][k] + Dis[k][j],Dis[i][j]);
}
void Min(double &ans,int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)   //!枚举所有存在的边
        {
            if(i==j || Cross[i][j].v==INF) continue; ///如果是自身或者没有边跳过,
            double time=Dis[1][i]; /// time = Dis(1->i) + K(i -> i+1) + Dis(i+1 -> n)
            time +=( Cal(Cross[i][j].v,Cross[i][j].a,time) + Dis[j][n]);
            ans=min(ans,time);
        }
}
int main()
{
   //freopen("F:\\test.txt","r",stdin);
    int n,m;
    while(~scanf("%d %d",&n,&m)&&(n||m))
    {
        Handle(n,m);  //!处理数据,存放信息
        Flyod(n);
        double MIN = 999999;
        for(int i=1;i<=m;i++) Min(MIN,n);
        printf("%.3lf\n",MIN);
    }
}
//没有记忆化的dijs TLE
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100
#define INF -1
typedef struct {double v,a;}UIO;
UIO Cross[2*MAXN][2*MAXN]; //!X,Y,Uio 题目说了只会有一条边
UIO Point[2*MAXN][2*MAXN]; //!复制Cross
bool Used[2*MAXN][2*MAXN];//!Cross检索表
typedef struct {int X,Y;}L;
L Line[1000];
void Init(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) Cross[i][j].v = i==j? 0:-1;
}
void Copy(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) Point[i][j] = Cross[i][j];
}
void Print_test(int n)
{
    for(int i=1;i<=n;i++,putchar('\n'))
        for(int j=1;j<=n;j++) printf("%4.3lf ",Point[i][j].v);
}
double Cal(double v,double a,double time)//!过该边时最优时间
{
    double t = log(v*log(a)/pow(a,time))/log(a);
    return t + pow(a,-t)*v;
}
void Handle(int n,int m)
{
    memset(Used,false,sizeof(Used));
    Init(n);
    for(int i=1;i<=m;i++)
    {
        int X,Y;double V,A;
        scanf("%d %d %lf %lf",&X,&Y,&V,&A);
        Cross[Y][X].v = Cross[X][Y].v = V;
        Cross[Y][X].a = Cross[X][Y].a = A;
        Line[i].X = X;  Line[i].Y = Y;
    }
}
double Dijkstra(int n,int Begin,int Acc)//!Acc为优化的边
{
    double time = 0;  Copy(n);
    int S[2*MAXN];S[1]=Begin;int J;int Last_J  = Begin;
    int T[2*MAXN];double Distance[2*MAXN];
    int CountS = 1,CountT = 0;
    for(int i=1;i<=n;i++)  Distance[i] = Point[Begin][i].v;
    for(int i=1;i<=n;i++) if(i!=Begin) T[++CountT]=i;
    while(CountS!=n)
    {
        double MIN_D=FLT_MAX;
        int Del;
          //!每次都优化这条边
            Point[Line[Acc].X][Line[Acc].Y].v = pow(Cross[Line[Acc].X][Line[Acc].Y].a,-time)*Cross[Line[Acc].X][Line[Acc].Y].v;
            Point[Line[Acc].Y][Line[Acc].X].v = Point[Line[Acc].X][Line[Acc].Y].v;

        for(int i=1;i<=CountS;i++)
            for(int j=1;j<=CountT;j++)
                if(T[j] && Distance[T[j]]!=(double)INF && MIN_D>=Distance[T[j]])
                        {MIN_D = Distance[T[j]];J = T[j];Del=j;}                    //!找出当前最短的边

        S[++CountS] = J;

        if((Last_J==Line[Acc].X && J==Line[Acc].Y) || (Last_J==Line[Acc].Y && J==Line[Acc].X ))
        {
            Point[Last_J][J].v = Cal(Point[Last_J][J].v,Point[Last_J][J].a,time);
            time = time + Point[Last_J][J].v;
        }
        else
            time = time + Distance[J];

        Distance[J] = time;

        for(int i=1;i<=CountT;i++)
        {
            if(Distance[T[i]]==(double)INF&&(Point[J][T[i]].v!=(double)INF))
                   Distance[T[i]] = Distance[J] + Point[J][T[i]].v ;
            else if(T[i]&&(Point[J][T[i]].v!=(double)INF)&&(Distance[T[i]]>Distance[J]+Point[J][T[i]].v))
                Distance[T[i]] = Distance[J] + Point[J][T[i]].v;
        }
        T[Del]=0;
        Last_J = J;
    }
    return Distance[J];
}
int main()
{
   // freopen("F:\\test.txt","r",stdin);
    int n,m;
    while(~scanf("%d %d",&n,&m)&&(n||m))
    {
        Handle(n,m);  //!处理数据,存放信息
        double MIN = FLT_MAX;

        for(int i=1;i<=m;i++) MIN = min(MIN,Dijkstra(n,1,i));//!每次优化第i条边
        printf("%.3lf\n",MIN);
    }
}
//记忆化的dijs TLE
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100
#define INF -1
typedef struct {double v,a;}UIO;
UIO Cross[2*MAXN][2*MAXN]; //!X,Y,Uio 题目说了只会有一条边
UIO Point[2*MAXN][2*MAXN]; //!复制Cross
bool Used[2*MAXN][2*MAXN];//!Cross检索表
typedef struct
{
    int Dis[2*MAXN];
    int time,CountS,CountT;
    int S[2*MAXN];
    int T[2*MAXN];
}Re;
Re Record[2*MAXN];//!表明到点X的无优化记录
typedef struct {int X,Y;}L;
L Line[1000];
void Init(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) Cross[i][j].v = i==j? 0:-1;
}
void Copy(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) Point[i][j] = Cross[i][j];
}
void Print_test(int n)
{
    for(int i=1;i<=n;i++,putchar('\n'))
        for(int j=1;j<=n;j++) printf("%4.3lf ",Point[i][j].v);
}
double Cal(double v,double a,double time)//!过该边时最优时间
{
    double t = log(v*log(a)/pow(a,time))/log(a);
    return t + pow(a,-t)*v;
}
void Handle(int n,int m)
{
    memset(Used,false,sizeof(Used));
    Init(n);
    for(int i=1;i<=m;i++)
    {
        int X,Y;double V,A;
        scanf("%d %d %lf %lf",&X,&Y,&V,&A);
        Cross[Y][X].v = Cross[X][Y].v = V;
        Cross[Y][X].a = Cross[X][Y].a = A;
        Line[i].X = X;  Line[i].Y = Y;
    }
}
double Dijkstra(int n,int Begin,int Acc,int flag)//!Acc为优化的边
{
    double time = 0;  Copy(n);
    int S[2*MAXN];S[1]=Begin;int J;int Last_J  = Begin;
    int T[2*MAXN];double Distance[2*MAXN];
    int CountS = 1,CountT = 0;
    if(!flag)
    {
        for(int i=1;i<=n;i++)  Distance[i] = Point[Begin][i].v;
        for(int i=1;i<=n;i++) if(i!=Begin) T[++CountT]=i;
    }
    else
    {
            for(int i=1;i<=n;i++)  Distance[i] = Record[Last_J].Dis[i];
            time = Record[Last_J].time;
            CountS = Record[Last_J].CountS;
            CountT = Record[Last_J].CountT;
            for(int i=1;i<=n;i++)  T[i] = Record[Last_J].T[i];
            for(int i=1;i<=n;i++)  S[i] = Record[Last_J].S[i];
    }
    while(CountS!=n)
    {
        if(!flag)
        {
            for(int i=1;i<=n;i++)  Record[Last_J].Dis[i] = Distance[i];
            Record[Last_J].time = time;
            Record[Last_J].CountS = CountS;
            Record[Last_J].CountT = CountT;
            for(int i=1;i<=n;i++) Record[Last_J].T[i] = T[i];
            for(int i=1;i<=n;i++) Record[Last_J].S[i] = S[i];
        }
        double MIN_D=FLT_MAX;
        int Del;
          //!每次都优化这条边
         if(flag==1)
         {
            Point[Line[Acc].X][Line[Acc].Y].v = pow(Cross[Line[Acc].X][Line[Acc].Y].a,-time)*Cross[Line[Acc].X][Line[Acc].Y].v;
            Point[Line[Acc].Y][Line[Acc].X].v = Point[Line[Acc].X][Line[Acc].Y].v;
         }
        for(int i=1;i<=CountS;i++)
            for(int j=1;j<=CountT;j++)
                if(T[j] && Distance[T[j]]!=(double)INF && MIN_D>=Distance[T[j]])
                        {MIN_D = Distance[T[j]];J = T[j];Del=j;}                    //!找出当前最短的边

        S[++CountS] = J;
        if(flag==1)
        {
            if((Last_J==Line[Acc].X && J==Line[Acc].Y) || (Last_J==Line[Acc].Y && J==Line[Acc].X ))
            {
                Point[Last_J][J].v = Cal(Point[Last_J][J].v,Point[Last_J][J].a,time);
                time = time + Point[Last_J][J].v;
            }
            else
                time = time + Distance[J];

        }
        else    time = time + Distance[J];

        if(flag==1) Distance[J] = time;

        for(int i=1;i<=CountT;i++)
        {
            if(Distance[T[i]]==(double)INF&&(Point[J][T[i]].v!=(double)INF))
                   Distance[T[i]] = Distance[J] + Point[J][T[i]].v ;
            else if(T[i]&&(Point[J][T[i]].v!=(double)INF)&&(Distance[T[i]]>Distance[J]+Point[J][T[i]].v))
                Distance[T[i]] = Distance[J] + Point[J][T[i]].v;
        }
        T[Del]=0;
        Last_J = J;
    }
    /*
    printf("{ ");
    for(int i=1;i<=CountS;i++) printf("%d ",S[i]);
    printf("}\n");
    */
    return Distance[J];
}
int main()
{
  //  freopen("F:\\test.txt","r",stdin);
    int n,m;
    while(~scanf("%d %d",&n,&m)&&(n||m))
    {
        Handle(n,m);  //!处理数据,存放信息
        double MIN = FLT_MAX;
        Dijkstra(n,1,2,0);
        for(int i=1;i<=m;i++) MIN = min(MIN,Dijkstra(n,1,i,1));//!每次优化第i条边
        printf("%.3lf\n",MIN);
    }
}

//

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值