Dijkstra

转载

——————————————

Dijkstra算法思想

如果图是不带负权的有向图或者无向图,我们可以利用贪心策略,从起点s每次扩展一个距离起点s最短的点,并且利用这个点,更新起点到其他点的距离。

Dijkstra算法流程

1、用一个数组a[i]记录其它点到起点s的最短距离,用一个数组b[i]标记是否得到从起点s到点i的最短距离
2、初始化数组a[i],修改其它点到起点s的距离,其中a[s]=0(起点到起点的距离为0),其他a[i]=∞(无限大)
3、选择一个没有被标记的点k并且d[k]的值是最小的
4、如果找不到,退出
5、标记点k,b[k]=true;
6、以k为中点,修改其他点到起点s的距离

Dijkstra算法流程模拟

下面用一个实例来具体说明:
在这里插入图片描述
(1)V0-V5 共6个节点,节点间路径已标出,现要求从V0到其余各节点的最短路径;
(2)有上面的算法流程可知,在使用Dijkstra算法时需要几个结构来保存我们想要的信息:
1.保存这张图的结构体
2.记录V0到其余各节点最短路径的数组(这里设为len[n+1])
3.记录某节点是否已找到最短路径的数组(这里设为note[n+1])
(3)接下来就是算法实现部分:
1.标记note[V0]=true;初始化 Len[]={0,∞,0,∞,10,∞,30,100}

2.第一次循环与V0相邻的有V2、V4、V5,其中V2距离最短为10,标记V2,并且以V2为中间点(路径为V0->V2->Vx)更新最短路表,此时V3被更新为10+50=60。

3.进入第二次循环,此时未被标记的有V1、V3、V4、V5,,其中从V0到这些的临时最短路分别为∞、60、30、100,从中找到最小的即V4,将V4标记为1,以V4为中间点(路径为V0->V4->Vx)更新最短路表,此时V3被更新为50,V5被更新为90。

4.进入第三次循环,此时未被标记的有V1、V3、V5,其中临时最短路分别为∞、50、90,从中找到最小的即V3,将V3标记为1,以V3为中间点(路径为V0->V4->V3->Vx)更新最短路表,此时V5被更新成60。

5.进入第四次循环,此时未被标记的有V1、V5,其中临时最短路分别为∞、60,找到V5,标记为1,以V5为中间点更新最短路表,此时没有元素被更新

6.进入第五次循环,这次循环没找到任何东西

7.退出循环,Len表中即为V0到其余各个节点的最短路径。

Dijkstra模板例题(没有优化O(n2))

题目来源:https://www.luogu.org/problemnew/show/P2299
【题目描述】
从1城市到n城市有很多条路,经过每条路所用的时间有可能不同,请问从1城市到n城市至少需要多少时间
【输入输出格式】
【输入格式】:
第一行有两个数n,m。n表示有n个停留站,m表示共有m条路。
之后m行,每行三个数ai bi ci,表示第ai​个停留站到第bi​个停留站需要ci​的时间。(无向)
【输出格式】:
一行,输出1到n最短时间。
【输入输出样例】
【输入样例】
5 8
1 2 3
2 3 4
3 4 5
4 5 6
1 3 4
2 4 7
2 5 8
1 5 100
【输出样例】
11
【说明】
时空限制:1000ms,128M
数据规模:
n≤2500 m≤2∗10的5次方

Dijkstra模板例题程序:

//Dijkstra
//https://www.luogu.org/problemnew/show/P2299
#include<fstream>
#include<cstring>
#include<iostream>
#define xb 2505
using namespace std;
int n,m,x,y,v;
int xy[xb][xb];
bool ifx[xb];//标记每个点最短路有没有被确定 
int weight[xb];//每个点到1城市的最小值 
void begin()
{
    memset(weight,0x7f,sizeof(weight));//把每个城市到1城市的所用时间调到最大
    weight[1]=0;//1到1的最小值为0 
    for(int i=1;i<=n;i++)
    {//初始化每个点到点1的值 
        weight[i]=xy[1][i];
    }
    ifx[1]=true;//1的最短路径已被确定
}
void Dijkstra()
{
    begin();//初始化
    for(int i=2;i<=n;i++)
    {
    	//打擂台找哪个城市到1城市所用的时间最少且这座城市的最短路径还没被确定 
        int num=0,sum=weight[0];
        for(int j=1;j<=n;j++)
        {
            if(!ifx[j]&&weight[j]<sum)
            {
            	sum=weight[j];
            	num=j;
            }
        }
        //如果找不到城市,退出 
        if(!num)break; 
        ifx[num]=true;//标记 
        for(int j=1;j<=n;j++)
        {
        	if(xy[num][j]+weight[num]<weight[j])
        	{//修改最短路径 
        		weight[j]=weight[num]+xy[num][j];
            }
        }
    }
}
int main()
{
    memset(xy,0x7f,sizeof(xy));//先调到最大值 
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
    	scanf("%d%d%d",&x,&y,&v);
    	if(v<xy[x][y])//x到y可能有很多条道路,要找出最小值 
    	xy[x][y]=xy[y][x]=v;
    }
    Dijkstra();
    printf("%d",weight[n]);
    return 0;
}

 

    【堆优化】
    题目:https://www.luogu.org/problem/P4779

    #include<queue>
    #include<fstream>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    int n,m,s,f;
    int next[200005],one[200005],g[200005],w[200005];
    LL weight[100005];
    bool note[100005];
    priority_queue<pair<int,int> >q;
    void Dij()
    {
    	memset(weight,0x7f,sizeof(weight));
    	weight[s]=0;
    	q.push(make_pair(0,s));//优先队列排序第一个优先级更高,顺序不能乱
    	while(q.size())
    	{
    		int now=q.top().second;
    		q.pop();
    		if(note[now])continue;
    		note[now]=true;
    		for(int i=one[now];i;i=next[i])
    		{
    			if(weight[g[i]]>weight[now]+w[i])
    			{
    				weight[g[i]]=weight[now]+w[i];
    				q.push(make_pair(-weight[g[i]],g[i]));
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&f,&g[i],&w[i]);
    		next[i]=one[f];
    		one[f]=i;
    	}
    	Dij();
    	for(int i=1;i<=n;i++)
    	{
    		printf("%d ",weight[i]);
    	}
    	return 0;
    }
    
    评论 4
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值