hdu 2680 最短路基础题 spfa实现 算法总结

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=2680

题目大意,就是一个人可以从多个起点开始出发,看到终点的最短路是多少。。只有可以运用和hdu2066一样的思想,对于每个起点可以看成是跟最最开始的点之间有一条权值为0的边。可以把最开始的点记做0点。那这样就可以使用单源最短路了。之前都没有用过spfa,今天来运用了一下。

算法大致流程是用一个队列来进行维护。初始时将源加入队列。每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。直到队列为空时算法结束。

这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法


总结一下,spfa就是在bellmanford的基础之上运用了一个队列来进行优化,思想就是如果这个点的最短距离被更新且他不在队列中就加入队列,如果在就不需要重复加入队列。。因为当他取出来的时候已经是最后一次被更新过的值了。这样就避免了多次重复的操作。。反观bellman-ford未优化的话,要对于每一条边进行n-1次的松弛操作,从而来试图更新每条边终点的最短距离,而spfa就直接从一开始的起点出发找出相邻的边,判断是否能更新,并判断是否要加入队列(已在队列中就不加),这样就减少了很多更新的次数。

例题 hdu 2680

代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define M 1009
#define INF 0x3f3f3f3f
struct edge
{
    int to,w;//保存边的信息,包括边的终点以及权值
};
int dis[M];  //最短距离的估计值(当前该点的最短距离)
bool inq[M]; //标记该点是否在队列之中
vector g[M]; //利用一个vector保存,g[i]表示以i为起点的所有边的信息
int n,m,ee;
void spfa(int u)
{
    for(int i = 0;i <= n;i++) //初始化
    {
        dis[i] = INF; //将估计值都初始化为INF
        inq[i] = false; //初始化为不在队列中
    }
    dis[u] = 0; //起点的估计值直接就是0
    inq[u] = true; //加入队列并进行标记
    queue q;
    q.push(u);
    while(!q.empty())
    {
        u = q.front();
        inq[u] = false;
        q.pop();
        for(int i = 0;i < g[u].size();i++)
        {
            int v = g[u][i].to; //找出这条边对应的终点
            int w = g[u][i].w;  //这条边对应的权值
            if(dis[v] > dis[u]+w) //如果终点的最短距离比起点的最短距离加上这条边的权值那么就更新
            {
                dis[v] = dis[u]+w;
                if(!inq[v]) //如果v点的最短距离有所更新并且不在队列中,就将其加入队列。
                {           //否则就不需要重复加入队列增加不必要的操作。
                    inq[v] = true;  //加入队列并标记
                    q.push(v);
                }
            }
        }
    }
}
int main()
{
    while(scanf("%d %d %d",&n,&m,&ee)==3)
    {
        for(int i = 0;i <= n;i++) //清空vector避免多kase相互影响
            g[i].clear();
        for(int i = 0;i < m;i++)
        {
            int a,b,c;
            scanf("%d %d %d",&a,&b,&c);
            edge e;
            e.to = b;e.w = c;
            g[a].push_back(e);
            //e.to = a;
            //g[b].push_back(e);  //题目中有说从a到b有这条路,并不是双向的。
        }
        int s;
        scanf("%d",&s);
        for(int i = 0;i < s;i++)
        {
            int a;
            scanf("%d",&a);
            edge e;
            e.to = a; e.w = 0;
            g[0].push_back(e); //将家里到起点的权值设为0
            //e.to = 0;
            //g[a].push_back(e);  //从起点到家里是否有路其实无关紧要,因为思考一下有路的话 起点到起点也是0,所以根本不影响结果
        }
        spfa(0);
        //printf("debug-----%d\n",dis[ee]);
        if(dis[ee]==INF)
            printf("-1\n");
        else
            printf("%d\n",dis[ee]);
    }
    return 0;
}


你可能感兴趣的:(图论_最短路)