P1629 邮递员送信(反向建边+对于floyd时间复杂度及,迪姐克斯拉算法等的新理解。)

本文详细分析了Floyd算法的时间复杂度,介绍了其在求解图中任意两点间最小距离的应用,以及迪杰斯特拉算法的运行结果和反向建边的概念。通过代码示例展示了如何在邻接矩阵中实现反向建边的方法。
摘要由CSDN通过智能技术生成

目录

         1.floyd算法时间复杂度分析

2.floyd算法的应用及代码

3.迪姐克斯拉算法的分析,反向建边的解释。


1.floyd算法时间复杂度分析

 对于图论来说,检查数据范围至关重要,那么我们现在看到节点的数目为一千,边的数目为一万。

 而floyd算法的时间复杂度是多少呢?

O(v3)也就是说我们的floyd算法几乎只与节点有关,而我们现在的数据范围恰好可行。

那么,现在回归题目,我们想一想运行一个floyd算法能够得到一个什么结果呢?

2.floyd算法的应用及代码

floyd算法运行结果是,图中任意两个点之间的最小长度都被求了出来,也就是说,我们可以很轻松地解决这个问题。(洛谷有开O2优化的选项,所以在代码中没有开启)

#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int dist[N][N];


int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m;
	cin >> n >> m;
	memset(dist, 0x3f, sizeof(dist));
	for (int i = 1; i <= m; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		dist[a][b] = min(dist[a][b], c);
	}
	for (int k = 1; k <= n; k++) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
			}
		}
	}
	int ans = 0;
	for (int i = 2; i <= n; i++) {
		ans += dist[1][i];
		ans += dist[i][1];
	}
	cout << ans << "\n";

	return 0;
}

3.迪姐克斯拉算法的分析及反向建边的解释。

现在,我们可以再来想一想迪姐克斯拉算法算法的运行结果是什么?

就是你指认的对应点到任意点的最小值,不是恰好符合题意嘛?

我们还要注意,最后我们还需要返回邮局,也就是说我们还需要反向求出最小值,我们该怎么求呢?

我们现在采用的迪姐克斯拉算法算法没有这样的反向最小值功能(如果非要搞得话比floyd复杂度都要高)

那么我们现在应该怎么办呢?

答案是建立返图,让多到一变成一到多,通俗来讲就是把邻接矩阵倒过来,这样的话发生了什么?

假设要问从x到1的最短路,为x->a->b->c->1,也就是说x->a,a->b,b->c,c->1都有路可走,那么我们想想,从x开始x->a,a->b,b->c,c->1的最短路。把邻接矩阵倒过来,不就是从1开始1->c,c->b,b->a,a->x的最短路吗?于是这时,我们把x->a,a->b,b->c,c->1这4条路径变为1->c,c->b,b->a,a->x,然后从1开始跑最短路,而它们的最短路是一样的。

如果我们用bellman算法怎么实现反向建边呢?

答案就是起点终点互换。

SPFA算法应该也一样,我会在之后的博客中详细解释这些,目前我们只需要看一看缔结克斯啦算法。

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int g[N][N];
int dist[N];
bool st[N];
int n,m;

void dijkstra()
{
	memset(dist,0x3f,sizeof(dist));
	memset(st,0,sizeof(st));
	dist[1]=0;
	for(int i=0;i<n;i++)
	{
		int t=-1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j]&&(t==-1||dist[t]>dist[j]))
			t=j;
		}
		st[t]=true;
		for(int j=1;j<=n;j++)
		{
			dist[j]=min(dist[t]+g[t][j],dist[j]);
		}
	}
}
void over()//这个函数主要用于将邻接矩阵进行翻转。
{
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			int t;
			t=g[i][j];
			g[i][j]=g[j][i];
			g[j][i]=t;
		}
	}
	
}
/*反向建边翻转哪里用的到呢?其实很简单,就是在一对多求一次,多对一还需要求一次的时候*/
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	memset(g,0x3f,sizeof(g));
    for(int i=1;i<=m;i++)
    {
    	int a,b,c;
    	cin>>a>>b>>c;
    	g[a][b]=min(g[a][b],c);
	}
	dijkstra();
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		ans+=dist[i];
	}
	over();//其实也就是反向建边。
	dijkstra();
	for(int i=1;i<=n;i++)
	{
		ans+=dist[i];
	}
	cout<<ans<<"\n";
	
	return 0;
}

最后最后最后,让我们思考思考,如何对dijkstra算法中邻接表存储进行反向建边吧。

在邻接矩阵中,我们主要是通过一个邻接矩阵的反转,那么其实这个也很简单,就是通过一个简单的交换起点终点来达到目的。。

add(a,b)

add(b+n,a+n)

这样在一个数组中就存放下了,我们只需要在往函数里面传递时传递1+n进行初始化就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值