XCPC第十一站,带你学会图论基本算法

基本图论
摘要由CSDN通过智能技术生成

在这里插入图片描述

        我们约定:以下n表示点的数目,m表示边的数目。

引子1——邻接表存储图的方法()(暂时不考虑重边和自环)

        现在我们有n个点(编号为1~n)和m条边,要用数组存储它们,我们可以怎么做呢?我们可以采取逐条加边的方法。假如我们要存储一条从a指向b的长度为w的边(注意,这里的a、b代表的是端点的具体编号而非端点被加入图中的次序号。为了不与下面的idx“编号”发生混淆,我们这里称a、b分别为加入的边的起点和终点的值)

const int K = ……(此处根据题目所给数据范围确定)
int h[K],e[K],ne[K],w[K],idx;
void add(int a,int b,int w)
{
   
	e[idx] = b;
	ne[idx] = h[a];
	w[idx] = w;
	h[a] = idx++;
}
//初始化
memset(h,-1,sizeof h);

        以上便是经典的邻接表加边函数的代码。下面我们详尽解释各变量、数组和代码的含义。至于为什么它们的含义如此我们将在下面的模拟图中看出。
        idx:编号(下文中的“编号”指的是在e数组中的编号,编号是几就是第几加一条被存进数组的边的终点的编号减1)。在最后的idx++执行完后,表示当前图中有idx条边,(idx+1)个点,下一条要加入的边(如果有的话)的终点在e数组中的下标为idx。
        h数组:存储编号的数组。h[k]表示值为k的点指向的下一个点的编号x(如果有多个“下一个点”,则表示的是最后加入的那一个)。这里很多资料说h数组存储的是头节点,但实际上,h数组的下标的值并不总是某条路径的头,因此这种说法是不正确的。
        w数组:存储边的长度。w[k]表示第(k+1)条被加入的边的长度
        ne数组:虚拟指针数组。邻接表保证了每个节点(除头节点无出边外)都必定有入边和出边(若某个节点没有真正的出边,就让它指向-1)。ne[k]表示编号为k的点的下一个点的编号
        e数组:存储值的数组。e[k]表示第(k+1)条加入的边的终点的值。
        那么,我们自然可以推导出遍历所有边的代码——

for(int i = h[u];i!=-1;i = ne[i])
………………

在这里插入图片描述

引子2——邻接矩阵存储图

        邻接矩阵存储图较为简单。我们开一个二维数组g,其中g[i][j]表示从值为i的点到值为j的点的距离。

一.朴素Dijkstra算法(适用于求解稠密图且所有边权威正的单源最短路问题,复杂度为O(n^2))

在这里插入图片描述

        基本思想:循环n次,找到每个点到起点的最短距离,并且用已经更新过的点去更新未被更新的点。

#include<iostream>
#include<cstring>
using namespace std;

const int N = 510;
//g[i][j]表示点i到点j的距离,dis[i]表示当前(注意是当前,未必是最小)第i个点到起点的最短距离
int g[N][N],dis[N],n,m;
//isConfirmed集合用于记录某点到起点距离的最小值是否已经被确定
bool isConfirmed[N];

int Dijkstra()
{
   
    dis[1] = 0;
    //之所以要有最外层循环,是因为我们一共有n个点,每次我们只能找到一个不在isConfirmed集合里的且离起点最近的点,我们一共要找n个这样的点。i = k表示当前最短路径上一共有了k个点
    for(int i = 1;i<=n;i++)
    {
   
        //t表示所有不在已经生成的路径中的点中离起点最近的点,初始时未确定,因此赋值-1
        int t = -1;
        //枚举所有点,找到不在isConfirmed集合中离起点最近的点赋予给t
        for(int j = 1;j<=n;j++)
        {
   
            if(!isConfirmed[j]&&(t==-1||dis[t]>dis[j]))  t = j;
        }
        
        isConfirmed[t] = true;
        //找到不在isConfirmed集合中离起点最近的点后,用该点更新它所有相邻点到起点的最短距离
        for(int j = 1;j<=n;j++)
            dis[j] = min(dis[j],g[t][j]+dis[t]);
    }
    //不要直接返回无穷大,否则可能出现无法预料的后果
    //0x3f和0x3f3f3f3f不同,它们仅在用memset初始化时可以互换!
    if(dis[n]==0x3f3f3f)    return -1;
    else return dis[n];
}

int main()
{
   
    //刚开始所有点到起点的距离都还没有确定,因此赋值为正无穷
    memset(dis,0x3f,sizeof dis);
    
    cin>>n>>m;
    
    for(int i = 1;i<=n;i++)
    {
   
        for(int j = 1;j<=n;j++)
        {
   
            //处理自环
            if(i==j)    g[i][j] = 0;
            else g[i][j] = 0x3f3f3f;
        }
    }
    
    while(m--)
    {
   
        int x,y,z;
        cin>>x>>y>>z;
        //若存在重边,取最短边
        g[x][y] = min(g[x][y],z);
    }
       
    printf("%d",Dijkstra());
    
    return 0;
}

二.堆优化版的Dijkstra算法(适用于求解稀疏图且所有边权为正的单源最短路问题,复杂度为O(mlogn))

        基本思想:在Dijkstra算法中有求不在isConfirmed集合中的到起点距离最短的点的操作,这一步可以用堆进行优化。
在这里插入图片描述

#
  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值