POJ_3255 Roadblocks(最短路)

博客介绍了POJ_3255 Roadblocks问题,探讨如何找到图中严格第二短的路径。文章提到了两种解决思路:一是使用最短路算法(如SPFA或Dijkstra)同时维护最短和次短距离;二是分别正序和逆序求最短路径数组,然后通过边连接找到次短路径。在实现过程中,作者指出SPFA算法因时间复杂度较低而更易上手,但Dijkstra在某些情况下可能导致超时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

找第二短的路。注意是严格的第二短,第二短一定是大于第一短的路径,并且路可以重复走。

思路:

首先注意到点的取值范围N(1-5000),要用邻接表去双向存储边表。

接下来考虑如何求第二短的路径,最好想的一个思路就是利用最短路的算法在维护最短距离的同时维护次短距离。另外设置一个dist1数组,当到达i点的最短路径更新时,将之前的最短距离存储到次短距离。在此注意设置dist[1] = 0时,dis1[1] = INF,到达起点的起始次短距离是INF。

另外一种方法是顺序求出最短路数组dist[],逆序求出最短路数组dist1[]。接下来遍历每一条边,对于某一边e(i,j),次短路一定等于dist[i]+e(i,j)+dist1[j],且不等于dist[N](最短路)。

因为对于边e(i,j)来说,假设该边不存在,则其他点的最短距离的性质是不变的,但是因为经过这条边(可能是重复经过),使得有了另外一条次短的路径。所以当我们在两边同时获得最短距离,同时枚举边时,就可以找到次短路径。

至于最短路的算法,SPFA或Dijkstra都可以选择,SPFA时间复杂度O(KE)(K<=2),Dijkstra时间复杂度O(E+VlgV),但是我的Dijkstra都TLE了。。。可能是测试数据的问题,但是SPFA感觉还是上手快一点。

代码实现:

方法一:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>

using namespace std;

const int MAX_V = 5005;
const int INF = 0x7ffffff;

struct Edge{
    int to;
    int cost;
    Edge(int to,int cost){
        this->to = to;
        this->cost = cost;
    }
};

int V,R;
int dis1[MAX_V];
int dis2[MAX_V];
bool mark[MAX_V];
vector<Edge> edge[MAX_V];
void SPFA(int v,int dis[]);
int main(){

    while( scanf("%d%d",&V,&R) != EOF ){
        for( int i = 1; i <= V; i++ ){
            dis1[i] = dis2[i] = INF;
            edge[i].clear();
        }
        int a,b,c;
        for( int i = 0; i < R; i++ ){
            scanf("%d%d%d",&a,&b,&c);
            edge[a].push_back(Edge(b,c));
            edge[b].push_back(Edge(a,c));
        }
        SPFA(1,dis1);
        SPFA(V,dis2);
        int res = INF;
        for( int i = 1; i <= V; i++ ){
            int len = edge[i].size();
            for( int j = 0; j < len; j++ ){
                Edge tmp = edge[i][j];
                int tt = dis1[i]+tmp.cost+dis2[tmp.to];
                if( (tt!=dis1[V]) && (tt<res) ){
                    res = tt;
                }
            }
        }
        printf("%d\n",res);
    }
}

void SPFA(int v,int dis[]){
    queue<int> que;
    que.push(v);
    dis[v] = 0;
    memset(mark,false,sizeof(mark));
    while( !que.empty() ){
        v = que.front();
        que.pop();
        mark[v] = false;
        int len = edge[v].size();
        for( int i = 0; i < len; i++ ){
            Edge tmp = edge[v][i];
            if( dis[tmp.to] > dis[v]+tmp.cost ){
                dis[tmp.to] = dis[v]+tmp.cost;
                if( mark[tmp.to] == false ){
                    que.push(tmp.to);
                    mark[tmp.to] = true;
                }
            }
        }
    }
    return ;
}

方法二:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

const int MAX_V = 5005;
const int INF = 0x7ffffff;

struct Edge{
    int to;
    int cost;
    Edge(int to,int cost){
        this->to = to;
        this->cost = cost;
    };
};

int V,R;
queue<int> que;
int dis[MAX_V];
int dis1[MAX_V];
bool mark[MAX_V];
vector<Edge> edge[MAX_V];
int main(){
    while( scanf("%d%d",&V,&R) != EOF ){
        for( int i = 1; i <= V; i++ ){
            dis[i] = INF;
            dis1[i] = INF;
            edge[i].clear();
            mark[i] = false;
        }
        int a,b,c;
        for( int i = 0; i < R; i++ ){
            scanf("%d%d%d",&a,&b,&c);
            edge[a].push_back(Edge(b,c));
            edge[b].push_back(Edge(a,c));
        }
        dis[1] = 0;
        que.push(1);
        mark[1] = true;
        while( !que.empty() ){
            int u = que.front();
            que.pop();
            mark[u] = false;
            int len = edge[u].size();
            for( int i = 0; i < len; i++ ){
                Edge tmp = edge[u][i];
                if( dis[tmp.to] > tmp.cost+dis[u] ){
                    dis1[tmp.to] = dis[tmp.to];
                    dis[tmp.to] = tmp.cost+dis[u];
                    if( mark[tmp.to] == false ){
                        que.push(tmp.to);
                        mark[tmp.to] = true;
                    }
                }
                if( (dis1[tmp.to]>tmp.cost+dis[u]) && (dis[tmp.to]<tmp.cost+dis[u]) ){
                    dis1[tmp.to] = tmp.cost+dis[u];
                    if( mark[tmp.to] == false ){
                        que.push(tmp.to);
                        mark[tmp.to] = true;
                    }
                }
                //比起Dijkstra这里要多处理一下,因为SPFA不带cost入队列
                //所以对于每一条边要把起点的次短距离也考虑上
                if( (dis1[tmp.to]>tmp.cost+dis1[u]) && (dis[tmp.to]<tmp.cost+dis1[u]) ){
                    dis1[tmp.to] = tmp.cost+dis1[u];
                    if( mark[tmp.to] == false ){
                        que.push(tmp.to);
                        mark[tmp.to] = true;
                    }
                }
            }
        }
        printf("%d\n",dis1[V]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值