欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图论模板

程序员文章站 2022-05-22 14:34:44
...

Dijkstra普通版实现

const int INF = ~0U >> 2;   //代表无穷大
const int VN = 100;        //最大顶点数
void Dijkstra(int s,int n)//s代表起点,n代表顶点个数
{
    bool vst[VN];
    memset(vst,0,sizeof(vst));
    for(int i = 1;i <= n;++i) d[i] = (i==s?0:INF);
    int kase = n; 
    while(kase--)
    {
        int x,m = INF;
        for(int y = 1;y <= n;++y) 
          if(!vst[y]&&d[y] <= m) m = d[x=y];
        vst[x] = 1;
        for(int y = 1;y <= n;++y)
        {
            if(d[y] > d[x]+w[x][y]) //松弛操作
            {
                d[y] = d[x] + w[x][y]; 
            }
        }
    }
}

除了求出最短路的长度外,使用Dijkstra算法也能很方便
地打印出从起点结点到所有结点的最短路,原理和动态规划中
的方案一样——从终点出发,不断顺着d[i]+w[i][j] = d[j]的边
(i,j)从结点j“退回”到结点i,直到回到起点。另外,仍然
可以用空间换时间,在更新d数组时维护“父亲指针”。
if(d[y] > d[x]+w[x][y])
{
d[y] = d[x]+w[x][y];
fa[y] = x;
}

Dijkstra优先队列加邻接表实现

#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <cstdio>
#include <cstring>
using namespace std;

typedef pair<int,int>pii;
const int INF = ~0U >> 2;
const int VN = 100;
int d[VN];
int top,w[2*VN],Head[VN],Key[2*VN],Next[2*VN];
void Add(int u,int v,int cost)
{
    w[top] = cost;
    Key[top] = v;
    Next[top] = Head[u];
    Head[u] = top++;
}
void Dijkstra(int s,int n)            //s代表起点,n代表顶点数
{
    priority_queue<pii,vector<pii>,greater<pii> > q;
    for(int i = 1;i <= n;++i) d[i] = (i==s?0:INF);
    q.push(make_pair(d[s],s));        //起点进入优先队列
    while(!q.empty())
    {
        pii u = q.top();
        q.pop();
        int x = u.second;   cout<<"x = "<<u.second<<" d = "<<d[x]<<endl;
        if(u.first != d[x])continue;  //已经算过,忽略
        for(int e = Head[x];e != -1;e = Next[e])
        {        
            int v = Key[e];
            if(d[v] > d[x] + w[e]){
               d[v] = d[x] + w[e];    //松弛成功,更新d[v]
               q.push(make_pair(d[v],v));//加入队列
            }
        }
    }
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        top = 0;
        memset(Head,-1,sizeof(Head));
        for(int i = 0;i != m;++i)
        {
            int x,y,cost;
            cin>>x>>y>>cost;
            Add(x,y,cost);
        }
        int s;
        cin>>s;
        Dijkstra(s,n);
        for(int i = 1;i <= n;++i)
           cout<<"i = "<<i<<" d = "<<d[i]<<endl;
        cout<<endl;
    }
    return 0;
}



解释一下上面的if(u.first != d[x])continue;这里也可以用一个标记数组来实现,但是要耗费一些内存,所以这里我就不用标记数组来判断该点是否实现过了。我解释一下为什么可以那样来判断呢?首先我们先想想,当有一个节点x已经更新过了压入队列.我们设当前再次遇到x节点的时候,我们此时记x为x'(x=x')则这次更新的d[x']一定比已经在队列里的小(d[x]>d[x']),所以此时x'(x=x')会再次压入队列。而x'(d[x'])先于之前x(d[x])已经在队列中的点先出队列。所以,之前在队列里的那个x(d[x])就无需再出队列进行再次对其他结点松弛了。

表述的可能有点乱,那就直接实例分析吧:

1 3 1

1 2 3

3 2 1

2 4 1

上面分别代表两个顶点和权值。

首先,我们设起点为1,则我们先把2,3压入队列,(此时d[2] = 3,d[3] = 1),所以下一次更新的是3这个节点,这时对2节点进行松弛操作将更新2节点即d[2] = 2,这时在将其压入队列,此时队列中就会存在两个2节点了,而我们在用2节点对其他点进行松弛的时候,只要用最短的一个就好了(即,d[2]=2)。ok,解释完毕。不知道你懂了没有。