2010年湖南省第六届大学生程序设计大赛 G题 “Repairing a Road” CSG - 1052(floyd 求函数极限)

题目链接


一.题目内容

给一个无向图,有n个点序号为1-n,m条边,每条边有两个值 vi 和 ai,表示从 u 到 v 需要vi 的时间,若修补这条边,设修补的时间为t, 则经过这条边的时间则变成 vi * ai^(-t) ,修补的时候不能走这条边,问若只能修补一条,从点1到点n最少需要多少时间

样例解释

input
3 2
1 2 1.5 1.8
2 3 2.0 1.5
2 1
1 2 2.0 1.8

output
2.589
1.976

样例一: 修补第二条边,修补时间为 1.5s,总共时间 = 1.5 + 2.0*1.5^(-1.5) = 2.589

样例二: 因为只有一条边,所以时间为函数 t+vi * ai^(-t) 的最小值 (t为修补的时间,t>=0)


二.解题思路

因为只能修一条边,所以可以直接枚举修每条边的最小结果,然后再取最小值。
假如修的是 ui-vi 这条边,那么首先要求 1-u 和 v-n的最小值,这时我们可以先用floyd跑一遍求出每个点到每个点的最小值,那么时间就等于 dis[1][u]+dis[v][n]+(u到v的时间)。
设u到v的时间为res,修补该边的时间为t,那么 res = vi * ai^(-t),很显然 t 越大越好,但是当 t 大于 dis[1][u] 时,到达u点后就会停留 t-dis[1][u] 的时间,那么最后所花的时间就是 t+res。
所以我们可以先把 t + vi * ai^(-t) 取最小值时的 t 求出来,然后再比较,若 t < dis[1][u],那么结果就为 dis[1][u] + res,所以要让res 尽可能地小, 所以 t 最大取 dis[1][u],若 t >= dis[1][u],则 t 即为 t + vi * ai^(-t) 取最小值时的 t。

f(t) = t + vi * ai^(-t)
f(t)取最小值时, f’(t)=1-vi * ai^(-t) * ln(a)=0
=>ai^(t)=vi * ln(a) => t=loga(vi * ln(a)) => t=ln(vi * ln(a)) / ln(a)
需要特殊处理当a=1的时候,防止ln(a)=0导致re,即 a=1时,t=0

三.解题代码

#include 
#include 
#include 
#include 
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
//#include
#include
using namespace std;
#define Case(x) printf("Case %d:",x)
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define rrep(i,l,r) for(int i=l;i>=r;--i)
#define memo(a) memset(a,0,sizeof(a))
#define out(x) cout<<(x)<<endl
#define sync ios::sync_with_stdio(false);
#define int long long
#define pa pair<int,int>
#define mkp make_pair
//const int MM=LLONG_MAX;
int dir[4][2]= {{-1,0},{0,-1},{1,0},{0,1}};
const int N=1e6+10,M=1e9+7;
//-----------//
struct node {
    int from,to;
    double v,a;
};
int n,m;
double dis[510][510];
queue<node>e;
signed main() {
    sync;
    while(cin>>n>>m&&n+m) {
        rep(i,1,n) rep(j,1,n) dis[i][j]=i==j?0:LLONG_MAX;
        rep(i,1,m) {
            int x,y;
            double a,v;
            cin>>x>>y>>v>>a;
            dis[x][y]=min(dis[x][y],v);
            dis[y][x]=min(dis[y][x],v);
            e.push(node{x,y,v,a});
            e.push(node{y,x,v,a});
        }
        rep(i,1,n) {
            rep(j,1,n) {
                rep(k,1,n) {
                    dis[j][k]=min(dis[j][k],dis[j][i]+dis[i][k]);
                }
            }
        }
        double ans=dis[1][n];
        while(e.size()) {
            node ee=e.front();
            e.pop();
            double eps=0.0000001;
            double tt=0;
            int x=ee.from,y=ee.to;
            double a=ee.a,v=ee.v;
            if(a-1>eps) tt=log(v*log(a))/log(a);
            if(tt<dis[1][x]) tt=dis[1][x];
            ans=min(dis[y][n]+tt+v*pow(a,-tt),ans);
        }
        cout<<fixed<<setprecision(3)<<ans<<endl;
    }
}


四.小结

你可能感兴趣的:(图论,湖南acm省赛,算法,图论,c++)