最短路(代码来源于kuangbin和百度)

最短路

最短路有多种算法,常见的有一下几种:Dijstra、Floyd、Bellman-Ford,其中Dijstra和Bellman-Ford还有优化;Dijstra可以用优先队列(或者堆)优化,Bellman-Ford也可以用队列优化,通常称为spfa。下面分别对这几种算法进行说明。

Dijstra适用于没有负权边的图,Bellman-Ford适用于有负权边的图,但是不能得到有负环的图的最短距离,只能判断有没有负环。Dijstra和Bellman-Ford都是单源最短路,Floyd算法是多源最短路。

一、Dijstra算法

Dijstra算法是不断将新的距离原点最短的点加入已经得到最短距离的集合,然后每加入一个点都更新各个点到源点的距离。这里有一个问题,设已经的到最短距离的点的集合为V,如何保证已经加入V的点已经得到了最短距离而之后源点到V中的点的最短距离不会改变了呢?假设i点已经加入V,而j点后来加入V,因为所有的边权值都是正的,如果存在dis[j]+cost[j][i]<dis[i](dis[i]表示i点到源点的距离,cost[i][j]表示i点到j点的边的权值),那么dis[j]<dis[i],如果dis[j]<dis[i],j点就应该先于i点加入V,与已知条件不符。所以在i点加入V之后,不会存在一个点作为中间点使得源点到i点的距离最小。

Dijstra算法的过程如下:

代码:

/*
*  单源最短路径,Dijkstra算法,邻接矩阵形式,复杂度为O(n^2)
*  求出源beg到所有点的最短路径,传入图的顶点数,和邻接矩阵cost[][]
*  返回各点的最短路径lowcost[],  路径pre[].pre[i]记录beg到i路径上的父结点, pre[beg]=-1
*  可更改路径权类型,但是权值必须为非负
*
*/
const int MAXN=1010;
#define typec int
const typec INF=0x3f3f3f3f;//防止后面溢出,这个不能太大
bool vis[MAXN];
int pre[MAXN];
void Dijkstra(typec cost[][MAXN],typec lowcost[],int n,int beg)
{
    for(int i=0;i<n;i++)
    {
        lowcost[i]=INF;
        vis[i]=false;
        pre[i]=-1;
    }
    lowcost[beg]=0;
    for(int j=0;j<n;j++)
    {
        int k=-1;
        int Min=INF;
        for(int i=0;i<n;i++)
            if(!vis[i]&&lowcost[i]<Min)
            {
                Min=lowcost[i];
                k=i;
            }
        if(k==-1)
            break;
        vis[k]=true;
        for(int i=0;i<n;i++)
            if(!vis[i]&&lowcost[k]+cost[k][i]<lowcost[i])
            {
                lowcost[i]=lowcost[k]+cost[k][i];
                pre[i]=k;
            }
    }
}

二、Dijstra+优先队列

将每次选取到达源点最近点的过程用一个优先队列代替,直接出队得到最近点和距离。但要把已经处理过的点涉及到的已经入队的略过不处理。

代码:

/*
*  使用优先队列优化Dijkstra算法
*  复杂度O(ElogE)
*  注意对vector<Edge>E[MAXN]进行初始化后加边
*/
const int INF=0x3f3f3f3f;
const int MAXN=1000010;
struct qnode
{
    int v;
    int c;
    qnode(int _v=0,int _c=0):v(_v),c(_c){}
    bool operator <(const qnode &r)const
    {
        return c>r.c;
    }
};
struct Edge
{
    int v,cost;
    Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[MAXN];
bool vis[MAXN];
int dist[MAXN];
void Dijkstra(int n,int start)//点的编号从1开始
{
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;i++)
        dist[i]=INF;
    priority_queue<qnode>que;
    while(!que.empty())
        que.pop();
    dist[start]=0;
    que.push(qnode(start,0));
    qnode tmp;
    while(!que.empty())
    {
        tmp=que.top();
        que.pop();
        int u=tmp.v;
        if(vis[u])
            continue;
        vis[u]=true;
        for(int i=0;i<E[u].size();i++)
        {
            int v=E[tmp.v][i].v;
            int cost=E[u][i].cost;
            if(!vis[v]&&dist[v]>dist[u]+cost)
            {
                dist[v]=dist[u]+cost;
                que.push(qnode(v,dist[v]));
            }
        }
    }
}
void addedge(int u,int v,int w)
{
    E[u].push_back(Edge(v,w));
}

三、Bellman-Ford算法

Bellman-Ford算法是对每条边进行松弛,重复|V|次,时间复杂度是O(V*E),伪代码如下:

for(i = 0; i < |V|; i++)
    for each edge(u, v) ∈  E
        RELAX(u, v)

算法简单,但是实际中不被采用,因为存在大量的无效松弛。

代码:

/*
*  单源最短路bellman_ford算法,复杂度O(VE)
*  可以处理负边权图。
*  可以判断是否存在负环回路。返回true,当且仅当图中不包含从源点可达的负权回路
* vector<Edge>E;先E.clear()初始化,然后加入所有边
*  点的编号从1开始(从0开始简单修改就可以了)
*/
const int INF=0x3f3f3f3f;
const int MAXN=550;
int dist[MAXN];
struct Edge
{
    int u,v;
    int cost;
    Edge(int _u=0,int _v=0,int _cost=0):u(_u),v(_v),cost(_cost){}
};
vector<Edge>E;
bool bellman_ford(int start,int n)//点的编号从1开始
{
    for(int i=1;i<=n;i++)dist[i]=INF;
    dist[start]=0;
    for(int i=1;i<n;i++)//最多做n-1次
    {
        bool flag=false;
        for(int j=0;j<E.size();j++)
        {
            int u=E[j].u;
            int v=E[j].v;
            int cost=E[j].cost;
            if(dist[v]>dist[u]+cost)
            {
                dist[v]=dist[u]+cost;
                flag=true;
            }
        }
        if(!flag)
            return true;//没有负环回路
    }
    for(int j=0;j<E.size();j++)
        if(dist[E[j].v]>dist[E[j].u]+E[j].cost)
            return false;//有负环回路
    return true;//没有负环回路
}

四、Spfa

Spfa是Bellman-Ford算法利用队列的优化,Bellman-Ford算法存在大量的无效松弛,spfa对它进行改进,对于每个点只松弛与这个点相关的边。

代码:

/*
*  单源最短路SPFA
*  时间复杂度  0(kE)
*  这个是队列实现,有时候改成栈实现会更加快,很容易修改
*  这个复杂度是不定的
*/
const int MAXN=1010;
const int INF=0x3f3f3f3f;
struct Edge
{
int v;
int cost;
Edge(int _v=0,int _cost=0):v(_v),cost(_cost){}
};
vector<Edge>E[MAXN];
void addedge(int u,int v,int w)
{
E[u].push_back(Edge(v,w));
}
bool vis[MAXN];//在队列标志
int cnt[MAXN];//每个点的入队列次数
int dist[MAXN];
bool SPFA(int start,int n)
{
    memset(vis,false,sizeof(vis));
    for(int i=1;i<=n;i++)
        dist[i]=INF;
    vis[start]=true;
    dist[start]=0;
    queue<int>que;
    while(!que.empty())
        que.pop();
    que.push(start);
    memset(cnt,0,sizeof(cnt));
    cnt[start]=1;
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        vis[u]=false;
        for(int i=0;i<E[u].size();i++)
        {
            int v=E[u][i].v;
            if(dist[v]>dist[u]+E[u][i].cost)
            {
                dist[v]=dist[u]+E[u][i].cost;
                if(!vis[v])
                {
                    vis[v]=true;
                    que.push(v);
                    if(++cnt[v]>n)  //cnt[i]为入队列次数,用来判定是否存在负环回路
                        return false;
                }
            }
        }
    }
    return true;
}

五、Floyd算法

Floyd算法是多源最短路,算法是将每对顶点(i,j)之间的所有其他点都进行松弛。

状态转移方程如下: map[i,j]=min{map[i,k]+map[k,j],map[i,j]};

代码:

#include<stdio.h>
#include<stdlib.h>
#define max 1000000000

int d[1000][1000],path[1000][1000];
int main()
{
    int i,j,k,m,n;
    int x,y,z;
    scanf("%d%d",&n,&m);

    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            d[i][j]=max;
            path[i][j]=j;
    }

    for(i=1;i<=m;i++)
    {
    scanf("%d%d%d",&x,&y,&z);
    d[x][y]=z;
    d[y][x]=z;
    }

    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                if(d[i][k]+d[k][j]<d[i][j]){
                    d[i][j]=d[i][k]+d[k][j];
                    path[i][j]=path[i][k];
                }
            }

    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++)
          if (i!=j) printf("%d->%d:%d\n",i,j,d[i][j]);

    int f,en;
    scanf("%d%d",&f,&en);
    while (f!=en){
        printf("%d->",f);
        f=path[f][en];
    }
    printf("%d\n",en);

    return 0;
}

时间: 2024-07-31 14:35:17

最短路(代码来源于kuangbin和百度)的相关文章

短URL代码也可利用百度API生成

案例:客户签收后,系统会回复一条短信给客户及一个点评服务的链接,由于链接过长,短信会自动截断,所以使用短链的方式发送. 代码如下: public string GetTinyUrl(string strLongUrl)    {        try        {            byte[] postData = Encoding.UTF8.GetBytes("url=" + strLongUrl);            var url = "http://dw

JSON的一个例子(代码来源于网上)

HTML代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Our superheroes</title> <link href="https://fonts.googleapis.com/css?family=Faster+One" rel="stylesheet"> <link

HDU 4010 Query on The Trees

题意: 一棵树  支持合并.分离.路径加权值.路径权值最大值 思路: LCT入门题  也是我的第一道-  代码来源于kuangbin巨巨  我只是整理出自己的风格留作模版- LCT比较好的入门资料是--<QTREE解法的一些研究> LCT基本做法就是先dfs建树  然后根据输入做上述4个操作 对于合并  就是把u转到树根  然后接在v上 对于分离  就是把u转到splay的根  然后切断与左子树的连接 对于路径加值  就是求出lca  然后包含u和v的子树以及lca点进行加值 对于路径求最值 

网站自动提交至百度站长收录平台函数(适用任何框架代码)

百度站长平台推出链接提交工具后让网站的内容有了新的快速提交收录方法. 好处在于可把你想要百度收录的内容快速提交给百度,当然是否收录要看度娘的脸色了,以下是函数内容 function bdurls($url){ $urls = is_array($url) ? $url : array($url); $api = 'http://data.zz.baidu.com/urls?site=www.zioor.com&token=NPsEfVDHBHij59dq'; $ch = curl_init();

php抓取百度快照、百度收录、百度热词程序代码

<?/*抓取百度收录代码*/function baidu($s){ $baidu="http://www.baidu.com/s?wd=site%3A".$s; $site=file_get_contents($baidu); //$site=iconv("gb2312", "UTF-8", $site); ereg("找到相关网页(.*)篇,", $site,$count); $count=str_replace(&q

百度自动推送代码的作用

在百度的搜索资源平台下的站点支持模块中有一个连接提交,下有一段代码叫做自动推送代码,只要在这个平台下认证了你的网站就可以,将自动推送代码加进去.我在https://www.jianzhumuju.com方圆扣这个站点上做了一下尝试,看看这段代码到底有什么作用,能够起到多大的效果,下面是这段代码的截图.在加入代码的时候,百度收录这个站点页面的索引量是1123,今天我们这段自动推送的代码加上,看看在半个月之后的效果.是不是收录增加,自动推送是不是能够让我们的网址真的能够及时推送给百度,百度的收录速度

感悟:百度万人协同规模下的代码管理架构演进

百度代码管理的挑战 百度拥有万人开发团队,近十万项目,每周代码自动检出的问题超二十万,每天发起评审超1万次.为了保证代码质量,我们要求代码提交前和提交后都进行自动化检查.为了加速编译和集成,我们有大规模的分布式编译系统和持续集成系统.百度C/C++语言是源码依赖,编译系统需要检出所有的依赖代码,这样代码库的访问压力呈指数级增长.这些都是百度代码管理面临的挑战,总结起来就是这三点:代码质量.规模协同和安全稳定的服务. 面对这三大挑战,代码开发协作平台重点解决代码管理五个方面的问题:代码托管.协同开

在网页中插入百度地图(实例)

步骤 1 2 3 如何在网页中插入百度地图呢? 2.切换城市,搜索需标注位置.(如下图 方法/步骤 1.进入:http://api.map.baidu.com/lbsapi/creatmap/(创建地图-百度地图API所见即所得工具,百度官方地址,大家放心使用) 切换城市,搜索需标注位置.(如下图:) 设置地图:大家可以对网站显示地图的宽高进行设置,其余选项不动. 添加标注:点击第一个图标后,在右侧找到自己的位置,单击鼠标左键可定位.标记图标处可更换图标形状,名称和备注填入位置相关信息.(如下图

有关“滑动门”代码研究

废话先不说,先上代码: /**************************************** 此代码来源于CSS-TRICKS,以下信息为博主发布: CREATED BY: Brandon Setter WEBSITE: http://Media-Sprout.com FOLLOW ME: twitter.com/bsetter CREATION DATE: 10/2009 VERSION 1.0 *****************************************/