[SPFA+路径输出] ZOJ3088 Easter Holidays

题目链接: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
#include
#include
#include
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;
    queueq;
    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};
    queueq;
    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);
    }
}


你可能感兴趣的:(ACM,题解)