[SPFA+路径输出] ZOJ3088 Easter Holidays

博客讲述了使用SPFA算法解决ZOJ3088 Easter Holidays问题,该问题要求找到最大距离与最小距离的比值,并输出对应路径。由于边的数量限制,博主采用SPFA算法实现,其复杂度为O(n*e),并讨论了用数组模拟邻接表提高效率,代码在OJ上运行时间为20ms。
摘要由CSDN通过智能技术生成

题目链接:Easter Holidays

题意:n个点m+k条边,任意s,e两点通过m边集的最大距离为MAX,通过k边集的最小距离是MIN,要求MAX比上MIN的最大值,还要输出从k中e到s,从m中s到e的路径。

做这题的时候真是心中万只草泥马奔腾。

要求任意点的最短路最长路,一般来说用Floyd(O(n^3)),然而这题范围用N^3复杂度高达10^9,显然不行。

但是注意到边数只有10^3,用SPFA的O(e)足够了,枚举N求出任意两点最大值最小值,复杂度O(n*e),10^6,1sec足够。

然而求最大值和最小值需要写两遍SPFA,虽然可以用if写在一起,但是分开写更直白一些,复制一遍,不用为难自己。

打算复习一下几个最短路算法,于是又回过头看了看以前写的代码,突然想起vector做邻接表的效率似乎很慢,于是又学习了用数组模拟邻接表的方法,而且也很简单,效率也快不少,温故而知新啊,我记得以前好像也看过这种方法,然而那时好像看不懂啊。说不定再手写queue可以更快?

代码在OJ跑20ms,这么快是数据太水了么,难道用Floyd可以水过?

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n, m, k;
int u[2005], v[2005], w[2005];
int fir[2005], nex[2005], cnt;
int path[1005][1005], path1[1005][1005];
int dis[1005][1005], dis1[1005][1005];
void add(int a, int b, int c){
    u[cnt] = a, v[cnt] = b, w[cnt] = c;
    nex[cnt] = fir[u[cnt]];
    fir[u[cnt]] = cnt ++;
}
void spfamax(int s){ //最长路
    bool vis[1005]={0};
    for(int i = 0; i <= n; ++i) dis[s][i] = 0;
    queue<int>q;
    q.push(s); vis[s] = 1; dis[s][s] = 0;
    while( !q.empty() ){
        int a = q.front(); q.pop(); vis[a] = 0;
        int k = fir[a];
        while(k != -1){
            int b = v[k], c = w[k];
            if(dis[s][b] < dis[s][a] + c){
                dis[s][b] = dis[s][a] + c;
                path[s][b] = a;
                if(!vis[b]){
                    vis[b] = 1;
                    q.push(b);
                }
            }
            k = nex[k];
        }
    }
}
void spfamin(int s){ //最短路
    bool vis[1005]={0};
    queue<int>q;
    for(int i = 0; i <= n; ++i) dis1[s][i] = 0xfffffff;
    q.push(s); vis[s] = 1; dis1[s][s] = 0;
    while( !q.empty() ){
        int a = q.front(); q.pop(); vis[a] = 0;
        int k = fir[a];
        while(k != -1){
            int b = v[k], c = w[k];
            if(dis1[s][b] > dis1[s][a] + c){
                dis1[s][b] = dis1[s][a] + c;
                path1[s][b] = a;
                if(!vis[b]){
                    vis[b] = 1;
                    q.push(b);
                }
            }
            k = nex[k];
        }
    }
}
void print(int path[], int e,int flag){
    if(flag) { if(path[e] == -1) return; } //避免s结点输出两次
    else { if(e == -1) return;}
    print(path, path[e],flag);
    printf("%d ",e);
}
int main(){
    int T,a,b,c;
    scanf("%d", &T);
    while( T-- ){
        memset(path, -1, sizeof(path));
        memset(path1, -1, sizeof(path1));
        scanf("%d %d %d", &n, &m, &k);
        
        cnt = 1;
        memset(fir, -1, sizeof(fir));
        for(int i = 1; i <= m; ++i){
            scanf("%d%d%d",&a, &b, &c);
            add(a, b, c);
        }
        for(int i = 1; i <= n; ++i) spfamax(i);
        
        cnt = 1;
        memset(fir, -1, sizeof(fir));
        for(int i = 1; i <= k; ++i){
            scanf("%d%d%d",&a, &b, &c);
            add(a, b, c);
        }
        for(int i = 1; i <= n; ++i) spfamin(i);
        
        double t=0;
        int s=0,e=0;
        for (int i = 1; i <= n; ++i){ //枚举s和e
            for (int j = 1; j <= n; ++j){
                if(i == j || dis[i][j] == 0 || dis1[j][i] == 0xfffffff) continue;
                if(dis[i][j]*1.0 / dis1[j][i] > t) t = dis[i][j]*1.0 / dis1[j][i], s = i, e = j;
            }
        }
        
        print(path1[e], s, 0);
        print(path[s], e, 1);
        printf("\n%.3f\n",t);
    }
}<strong>
</strong>


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值