图论模板

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,解释完毕。不知道你懂了没有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值