最短路算法汇总

来源:互联网 发布:mac可以加硬盘吗 编辑:程序博客网 时间:2024/05/18 16:39

     校赛完了,这次校赛,做的很差,一个算法题没有,2个水题,1个贪心,概率DP,DP,数论题。DP还没开始研究,数论根本不会,数学太差了,省赛时卡数论,校赛依然卡数论,我擦,还是得继续学习啊!

    一把锈迹斑斑的剑,只有不断的磨砺,才能展露锋芒!

以下为最短路总结:


最短路问题可分为:


一、单源最短路径算法,解决方案:Bellman-Ford算法,Dijkstra算法,SPFA

二、每对顶点间的最短路径算法:Floyd;


(1).Dijkstra算法:


(经典的算法,可以说是最短路问题的首选事例算法,但是不能处理带负权的边,因为该算法要遍历的点过多,效率低下,用时长,仅限于小数据,不常用)
  
   基本思想:
        Dijkstra算法,本质上是利用贪心思想来不停的来进行贪心选择,查找最优解,开辟一个s[],用来存放这些点,

  dis[]用来存放所能经过的每个点的最短距离,并进行dis[]的更新操作。注意:只能处理无负权的边


  
模版如下:
   int m,n,map[max][max],dis[max],s[max];//int prev[max];//当前点的上一个节点       void Dijkstra(int v0)  {    for(int i = 0;i<n;i++) //初始化dis[]    {        s[i] = 0;        dis[i] = map[v0][i];     /*   if(i!=v0 && map[v0][i]<inf)        prev[i] = v0         else         prev[i] = -1;  */    }    s[v0] = 1;    dis[v0] = 0;    for(int i = 2;i<=n;i++)    {        int u = v0,min = inf;        for(int j = 0;j<n;j++)//贪心查找还未存储的最优解的点,并储存        {            if(!s[j] && dis[j] < min)//当前最短距离            {                u = j;//记录下标                min  = dis[j];            }        }        s[u] = 1;        for(int j = 0;j<n;j++)//更新dis[]数组        {            if(!s[j] && map[u][j] < inf && dis[u] + map[u][j] < dis[j])            {                 dis[j] = map[u][j] + dis[u];                 //prev[j] = u;             }                        }    }  }


优先队列的Dijkstra

HDU 1874

#include <iostream>#include <cstdlib>#include <cstring>#include <cstdio>#include <queue>#include <vector>const int N = 10001;const int INF = 1e7;using namespace std;struct node{    int x,d;    node(){}    node(int a,int b)    {        x=a; d=b;    }    bool operator <(const node & a)const    {        if(d == a.d)           return  x < a.x ;        else           return d > a.d;    }};vector <node> ma[N];int dis[N],n;void Dijkstra(int s){    for(int i=0;i<=n;i++)        dis[i]=INF;    dis[s]=0;    priority_queue<node> q;    q.push(node(s,dis[s]));    while(!q.empty())    {        node p=q.top();        q.pop();        int s = ma[p.x].size();        for(int i=0;i<s;i++)        {            node pp=ma[p.x][i];            if(dis[pp.x]>p.d+pp.d)            {                dis[pp.x]=p.d+pp.d;                q.push(node(pp.x,dis[pp.x]));            }        }    }}int main(){    int a,b,c,m,s,e;    while(scanf("%d%d",&n,&m)!=EOF)    {        for(int i=0;i<=n;i++)            ma[i].clear();        while(m--)        {            scanf("%d%d%d",&a,&b,&c);            ma[a].push_back(node(b,c));            ma[b].push_back(node(a,c));        }        scanf("%d%d",&s,&e);        Dijkstra(s);        if(dis[e]!=INF)            printf("%d\n",dis[e]);        else            puts("-1");    }    return 0;}
// 题意: 有n*m矩阵,从起点(sx,sy)出发,可以上下左右四个方向移动,// 若两个位置上是相同字符,则花费为0,否则为1,求到终点的最短距离// 用Dijkstra算法解决,但会 TLE  ,需要用 优先队列 优化时间#include <iostream>        // 邻接矩阵+优先队列实现Dijkstra算法#include <stdio.h>#include <queue>#include <cstring>using namespace std;                                const int INF=300000;const int MAXN=250000;int n,m,distD[MAXN],done[MAXN];    int sx,sy,tx,ty;    //起点(sx,sy)和终点(tx,ty)int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};char data[500][500];typedef pair<int,int> pii;void Dijkstra(int st)    //从源点st到其余各顶点的最短路径长度{    priority_queue<pii, vector<pii>, greater<pii> > q;    fill(distD,distD+n*m,INF);        //结点下标从0开始,共有 m * n 个结点    distD[st]=0;    fill(done,done+n*m,0);    q.push(make_pair(distD[st], st));    while(!q.empty())     {        pii u = q.top();        q.pop();        int p = u.second;        if(done[p])            continue;        done[p] = 1;        if( p == tx * m + ty )    // 如果探测到终点即可退出        {            printf("%d\n",distD[p]);                break;        }        for(int d=0;d<4;++d)        {            int newx=p/m+dx[d],newy=p%m+dy[d];            int newp=newx*m+newy;            if(newx>=0&&newx<n&&newy>=0&&newy<m&& !done[newp] )            {                int w = ( data[p/m][p%m] == data[newx][newy] ) ? 0 : 1 ;                if( distD[p] + w < distD[newp] )                {                    distD[newp]=distD[p]+w;                    q.push(make_pair(distD[newp], newp));                }            }        }    }}int main(){    while(scanf("%d%d",&n,&m)!=EOF)        // n 行 m 列    {        for(int i=0;i<n;++i)        {            scanf("%s",data[i]);        }        scanf("%d%d%d%d",&sx,&sy,&tx,&ty);        Dijkstra(sx*m+sy);    // 顶点坐标[x,y],则在队列对应的下标是 x * m + y    }    return 0;}





(2).Bellman-Ford
     
     (用来判断是否有回环,可处理带负权边 时间复杂度O(V*E).)
  
     基本思想:(DP思想,查找最优解,并不断的进行松弛操作,但是算法浪费了许多时间做冗杂的松弛操作,效率降低) 无向图
void add(){    scanf("%d%d%d",&u,&v,&w);    edge[l].u = u; edge[l].v = v; edge[l++].w = w;    edge[l].u = v; edge[l].v = u; edge[l++].w = w;}int Bellman(int cnt){    int i,j,flag;    for(i = 1;i <= N - 1;i++)    {        flag = 0;        for(j = 0;j <= count - 1;j++)          {              if(dis[edge[j].u]> dis[edge[j].v] + edge[j].w)                {    //松弛计算                dis[edge[j].u] = dis[edge[j].v] + edge[j].w;//更新dis[]数组,使其存储 源点->当前点的最短距离                flag = 1;                }          }        if(flag==0)//如果查找完,或者有些点达不到,跳出->优化            break;    }    for(i = 0;i < count;i++)//判断是否有负环路        {            if(dis[edge[i].u] > dis[edge[i].v] + edge[i].w)//更新完dis[]后,如果还存在 该成立条件,则存在负环路            return 1;                                             }                                              //  如1->2 权值2    return 0;                                         //     2->3 权值5                                                      //     3->1 权值-8                                                    //       1->2->3->1 一次循环为-1 二次-2  三次....这就叫负环}

(2)FLOYD
        这个实在没什么好说的,做题目时,只要确定的 时间不超,就可以用,前提是记下,时间复杂度O(n*n*n).

ma[u][v] 储存的就是u->v的距离
 
 void init() {           for(i=0;i<n;i++)        for(j=0;j<n;j++)        {           map[i][j] = (i==j)?0:inf;        }  }void floyd(){         for(k=0;k<n;k++)      {          for(i=0;i<n;i++)            {                for(j=0;j<n;j++)                {                  if(map[i][k]!=inf && map[k][j]!=inf && map[i][j]>map[i][k] + map[k][j] )                   {                    map[i][j]=map[i][k]+map[k][j];                   }                 }             }      }}

(4)SPFA


     书上说是在bellman-ford的基础上,添加一个队列操作,减少了其不必要的松弛操作。

我个人认为是在BFS搜索的基础上加了一步所谓的松弛操作,至于为什么叫松弛,不懂。

但是SPFA优化的很棒,以后最短路问题,尽量用它。无向图


矩阵式

void SPFA(int s,int e)   S点 到e点{    int l,r,i;    l=r=0;    memset(vt,0,sizeof(vt));    for(i=s;i<e+1;i++)    dis[i]=inf;    dis[s]=0;    q[r++]=s;//进队列    vt[s]=1;//标记 进队列1            //不在队列为0    while(l<r)    {        int p=q[l++];//出队列        for(i=0;i<n;i++)//枚举与p相连的点        {            if(dis[i]>dis[p] + map[p][i])//松弛            {                dis[i] = dis[p] + map[p][i];                if(!vt[i])                {                    q[r++] = i;                    vt[i] = 1;                 }            }        }        vt[p] = 0;    }    if(dis[e]!= inf)    printf("%d\n",dis[e]);}

前向星式

struct node{    int u,v,w;    int next;}edge[40000];int head[40000];void init(){     memset(vis,0,sizeof(vis));     memset(head,0,sizeof(head));     for(int i = 0;i<n;i++)     {            dis[i] = INF;     }     t = 1;}int s,e;void add(int a,int b,int c){   edge[t].v = b;   edge[t].w = c;   edge[t].next = head[a];   head[a] = t++;}void SPFA(){    queue<int>q;    q.push(s);    vis[s] = true;    dis[s] = 0;    while(!q.empty())    {        int p = q.front();        q.pop();        vis[p] = 0;        if(head[p])        {            for(int pp = head[p];pp!=0;pp = edge[pp].next)            {                if(dis[p] + edge[pp].w < dis[edge[pp].v])                {                    dis[edge[pp].v] = dis[p] + edge[pp].w ;                      if(!vis[edge[pp].v])                   {                    q.push(edge[pp].v);                    vis[edge[pp].v] = true;                  }                }             }        }    }    if(dis[e]<INF)        printf("%d\n",dis[e]);    else        printf("-1\n");}


 邻接链表式

struct node{    int u,v,w;    node *next;}*head[40000];void init(){     memset(vis,0,sizeof(vis));     memset(head,NULL,sizeof(head));     for(int i = 0;i<n;i++)     {        dis[i] = INF;     }     t = 1;}int s,e;void add(int a,int b,int c){    node *p = new node;    p->v = b;    p->w = c;    p->next = head[a];    head[a] = p;}void SPFA(){    queue<int>q;    q.push(s);    vis[s] = true;    dis[s] = 0;    while(!q.empty())    {        int p = q.front();        q.pop();        vis[p] = 0;        node *pp;            for(pp = head[p];pp!=NULL;pp = pp->next)            {                if(dis[p] + pp->w < dis[pp->v])                {                    dis[pp->v] = dis[p] + pp->w;                      if(!vis[pp->v])                   {                    q.push(pp->v);                    vis[pp->v] = true;                  }                }        }    }    if(dis[e]<INF)        printf("%d\n",dis[e]);}

2 0
原创粉丝点击