dijkstra


dijkstra可以求出一个点到所有点的最短路。

链式前向星其实就是静态建立的邻接表,时间效率为O(m),空间效率也为O(m)。遍历效率也为O(m)。

【模板】单源最短路径(弱化版)

题目P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题解:     1.首先明确这是一个有向图求最短路的问题,这个题目我用的是链式前向星dijkstra算法。

                2.4个数组,结构体lu[]中的to代表这个点指向的这一个点,next表示与这个边起点相同的上一条边的编号,v代表权重。

                                head[](存储以 i 为起点的最后一条边的编号),head数组与lu[]相关。

                                dis[](记录权重的数组),

                               v[](记录这个点的所有边是否都考虑完的数组);

                3.初始化数组,dis[]变成无限大,v[]变成没有访问的状态

                4.将出发点的距离设置成0,标记一下出发点,找到与出发点有关的点,判断一下点是否被访问过,更新相关点的dis的值。然后找下一个出发点(这个点没有被标记过且dis[]数组中值最小的)。一直循环到最后一个点的距离更新。

                6.最后的dis[]数组中存的数就是一开始的出发点到所有点的最小路的长度。

代码如下

#include<bits/stdc++.h>
using namespace std;
int cnt,head[10009],n,m,s,dis[10009],v[10009];
#define max 2147483647
struct node{
	int to,next,dis;
}lu[500009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
	cnt++;
    lu[cnt].to = v; //终点
    lu[cnt].dis = w; //权值
    lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值 
    head[u] = cnt;//更新以u为起点上一条边的编号
}
int main()
{
	cin>>n>>m>>s;
	int x,y,z;
	int i,j;
	for(i=1;i<=n;i++)
	{
	dis[i]=max;//dis[]变成无限大,v[]变成没有访问的状态
	v[i]=0;
	}
	for(i=1;i<=m;i++)
	{
		cin>>x>>y>>z;
		add(x,y,z);//有向图
	}
	int ans;
	dis[s]=0;
	ans=s;
	for(i=1;i<=n;i++)
	{
		v[ans]=1;
		for(j=head[ans];j!=0;j=lu[j].next)//找到最小的距离 
		{
			int a=ans,b=lu[j].to,c=lu[j].dis;
			if(dis[b]>dis[a]+c&&v[b]==0)//没有访问过 
			{
			dis[b]=dis[a]+c;
			}
		}
		long long int min = max;//找下一个出发点
        for(x=1;x<=n;x++) 
        {
            if (v[x]==0&&min>dis[x]) 
            {
                min=dis[x];
                ans=x;
            }
        }
	}
	for(i=1;i<=n;i++)
	cout<<dis[i]<<' ';
	return 0;
 } 

[USACO09OCT]Heat Wave G

题目P1339 [USACO09OCT]Heat Wave G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题解:    1.首先明确这是一个无向图求两个点之间最短路径。第一想到的是Floyd算法,但是Floyd会超时。然后就想到了dijkstra算法,尝试了朴素版的,结果只能ac50,然后就换成了链式前向星dijkstra算法。

                2.与上面的题是一样的,不过多赘余,改一下输入,输出就可以了。因为是无向图所以add函数要调用两次。

ac100代码如下

#include<bits/stdc++.h>
using namespace std;
int cnt,head[10009],n,m,s,g,dis[10009],v[10009];
#define max 2147483647
struct node{
	int to,next,dis;
}lu[500009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
	cnt++;
    lu[cnt].to = v; //终点
    lu[cnt].dis = w; //权值
    lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值 
    head[u] = cnt;//更新以u为起点上一条边的编号
}
int main()
{
	cin>>n>>m>>s>>g;
	int x,y,z;
	int i,j;
	for(i=1;i<=n;i++)
	{
	dis[i]=max;
	v[i]=0;
	}
	for(i=1;i<=m;i++)
	{
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z);//无向图
	}
	int ans;
	dis[s]=0;
	ans=s;
	for(i=1;i<=n;i++)
	{
		v[ans]=1;
		for(j=head[ans];j!=0;j=lu[j].next)//找到最小的距离 
		{
			int a=ans,b=lu[j].to,c=lu[j].dis;
			if(dis[b]>dis[a]+c&&v[b]==0)//没有访问过 
			{
			dis[b]=dis[a]+c;
			}
		}
		long long int min = max;
        for(x=1;x<=n;x++) 
        {
            if (v[x]==0&&min>dis[x]) 
            {
                min=dis[x];
                ans=x;
            }
        }
	}
	cout<<dis[g];
	return 0;
 } 

  这里说一下朴素版dijkstra。

与上面的原理一样,我只要是说一下代码的区别

1.这里是用二维数组r[][]来存储所有权重的消息,其他的是一样的。但是二维数组会比较耗内存。

2.这里要初始化r[][[]数组消息,除了到本身,其实全部变成无限大值。

3.for (i = 1; i <= n; i++) {   dis[i] = r[s][i]; },这一步是把dis[]数组的值变成与出发点距离有关的值。

4.与上面不一样的是这个是先找到与出发点有关的最小距离的值,设置成下一个出发点,然后再更新dis[]数据。

#include<bits/stdc++.h>
using namespace std;
int r[2525][2525],dis[2525],v[2525];//记录权重的数组
int n,m,s,g;
int main() {
	cin>>n>>m>>s>>g;
	int max=1009;
	int i, j, k,z,y,x,ans;
	for (i = 1; i <= n; i++) {
		for (j = 1; j <= n; j++) {
			if (i == j)
				r[i][j] = 0;
			else
				r[i][j] = max;
		}
	}
	for (i = 1; i <= m; i++) {
		cin >> x >> y >> z;
			r[x][y] = z;
			r[y][x] = z;//无向图
	}
	for (i = 1; i <= n; i++) {
		dis[i] = r[s][i];
		v[i]=0;
	}
	v[s] = 1;
	for (i = 1; i <= n ; i++) {
		int min=max;//找到与出发点有关的最小距离的值,设置成下一个出发点
		for (j = 1; j <= n; j++) {
			if (v[j] == 0 && dis[j] < min) {
				min = dis[j];
				ans = j;
			}
		}
		v[ans] = 1;//更新
		for (k = 1; k <= n; k++) {
				if (dis[k] > dis[ans] + r[ans][k])
					dis[k] = dis[ans] + r[ans][k];
			}
	}
	cout<<dis[g];
	return 0;
}

​

【模板】单源最短路径(标准版)

题目P4779 【模板】单源最短路径(标准版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题解:    1.与第一个题相同的题目,这个题要用链式前向星dijkstra算法+优先队列。

                2.与上面的思路相同这里只要讲一下优先队列。自定义优先队列的排序结构。

struct node1{
	int x;
	int y;
	bool operator<(const node1&x)const{return y>x.y;}//小顶堆 
};
priority_queue<node1>c;
c.push({s,0});//入队,优先对列按dis[](即结构体中的y)数组从小到大排序
t=c.top().x;//这里取出的是上面的s。
c.pop();//出队
//这里的int x;
	    int y;代表要向队列中传入两个数

         3.这里循环与t有关的点,更新这些点的dis[]的值,如果没有被访问过就入队。

for(i=head[t];i!=0;i=lu[i].next)
		{
			int a=t,b=lu[i].to,v1=lu[i].d;
			if(dis[b]>dis[a]+v1)
			{
				dis[b]=dis[a]+v1;
				if(v[b]==0)
				c.push({b,dis[b]});//入队
			}
		}

代码如下

#include<bits/stdc++.h>
#include<queue>
using namespace std;
int cnt,n,m,s,dis[100009],v[1000009],head[100009];
#define max 2147483647
struct node1{
	int x;
	int y;
	bool operator<(const node1&x)const{return y>x.y;}//小顶堆 
};
struct node{
	int to,next,d;
}lu[200009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
	cnt++;
    lu[cnt].to = v; //终点
    lu[cnt].d = w; //权值
    lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值 
    head[u] = cnt;//更新以u为起点上一条边的编号
}
int main()
{
	cin>>n>>m>>s;
	int x,y,z;
	int i,j;
	for(i=1;i<=n;i++)
	{
	dis[i]=max;
	v[i]=0;
	}
	for(i=1;i<=m;i++)
	{
		cin>>x>>y>>z;
		add(x,y,z);//有向边 
	}
	priority_queue<node1>c;
	dis[s]=0;
	c.push({s,0});//入队,优先对列按dis[]即结构体中的y数组从小到大排序
	int t;
	while(!c.empty())
	{
		t=c.top().x;
		c.pop();//出队
		if(v[t])
		continue;
		v[t]=1;
		for(i=head[t];i!=0;i=lu[i].next)
		{
			int a=t,b=lu[i].to,v1=lu[i].d;
			if(dis[b]>dis[a]+v1)
			{
				dis[b]=dis[a]+v1;
				if(v[b]==0)
				c.push({b,dis[b]});//入队
			}
		}
	}
	for(i=1;i<=n;i++)
	cout<<dis[i]<<' ';
	return 0;
 } 

邮递员送信

题目P1629 邮递员送信 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题解:     1.首先明确这是一个有向图求最短路的问题,很简单只要把来的值算一遍,去的值算一遍加在一起就可以了,我用的是链式前向星dijkstra算法+优先队列。

                2.一开始我求回来的值用的是循环,结果时间超限了,怎么办?把邻接矩阵倒过来!再开辟一个相同的空间,记录回来的值不就好了。其他的过程都是一样的,这样的话回来就不要循环了。

                3.因为时间超限这里我加了快读。

                4.因为要调用两次dij()函数,所以在dij()中对dis[]和v[]初始化。且注意循环的最后值。

代码如下

#include<bits/stdc++.h>
#include<queue>
using namespace std;
int cnt,n,m,dis[100009],v[1000009],head[100009];
#define max 2147483647
int read()//快读 
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*f;
}
struct node1{
	int x;
	int y;
	bool operator<(const node1&x)const{return y>x.y;}//小顶堆 
};
priority_queue<node1>c;//优先队列 
struct node{
	int to,next,d;
}lu[200009];//邻接表存图
void add(int u, int v, int w)//加边,u起点,v终点,w边权
{
	cnt++;
    lu[cnt].to = v; //终点
    lu[cnt].d = w; //权值
    lu[cnt].next = head[u];//以u为起点上一条边的编号,也就是与这个边起点相同的上一条边的编号,防止覆盖原来的值 
    head[u] = cnt;//更新以u为起点上一条边的编号
}
void dij(int s)
{
	int i;
	for(i=1;i<=2*n;i++)
	{
	dis[i]=max;
	v[i]=0;
	}
	dis[s]=0;
	c.push({s,0});
	int t;
	while(!c.empty())
	{
		t=c.top().x;
		c.pop();
		if(v[t])
		continue;
		v[t]=1;
		for(i=head[t];i!=0;i=lu[i].next)
		{
			int a=t,b=lu[i].to,v1=lu[i].d;
			if(dis[b]>dis[a]+v1)
			{
				dis[b]=dis[a]+v1;
				if(v[b]==0)
				c.push({b,dis[b]});
			}
		}
	}
}
int main()
{
	n=read();m=read();
	int x,y,z;
	int i,j;
	for(i=1;i<=m;i++)
	{
		x=read(),y=read(),z=read();
		add(x,y,z);//有向边 
		add(y+n,x+n,z);
	}
	dij(1);
	int ans=0;
	for(i=2;i<=n;i++)
	ans+=dis[i];//去的路程 
	dij(1+n);
	for(i=2+n;i<=2*n;i++)
	ans+=dis[i];
	cout<<ans;
	return 0;
 } 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值