最短路之板子の个人理解及汇总(自存)

目录

单源(某一个点到其余点)

        全正边

                Dijkstra  O(n^2)

                Dijkstra(链式前向星)O(mlogn)

        存在负边

                Bellman-ford  O(nm)

                SPFA  O(m)~O(nm)

多源(任意两点互相走)

               floyd(全正边)  O(n^3)        


单源(某一个点到其余点)

        全正边

        Dijkstra  O(n^2) 

1.Dijkstra  O(n^2)

const int maxn=1e6+10;
const int inf = 0x3f3f3f3f;
int dist[maxn], box[maxn], mp[1010][1010];
//初始化 
memset(dist, 0x3f, sizeof dist);
memset(mp, inf, sizeof mp);

//存图
mp[a][b] = mp[b][a] = min(mp[a][b], value);  //双向且考虑重边 

void dij(int start)
{
	for (int i = 0; i < n; i++) {
		ll t = -1;
		dist[start] = 0;
		for (int j = 1; j <= n; j++) {
			if (!box[j] &&(t == -1 || dist[t] > dist[j])) {
				t = j;
			}
		}
		box[t] = 1;
		for (int j = 1; j <= n; j++) {
			dist[j] = min(dist[j], dist[t] + mp[t][j]);
		}
	}
}

Dijkstra(链式前向星)O(mlogn) 

2.Dijkstra(链式前向星)O(mlogn)

#define ll long long
#define pii pair<int,int>

const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int h[maxn];//存每个点对应的链的头 
int dist[maxn];
bool box[maxn];

//用前初始化规则
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
memset(box, 0, sizeof box);

struct st{
	int w; //权值 
	int ne;//存链的头 
	int e; //存入图的尾值 
}yy[maxn];

int idx = 1; //输入的线段的位置 =>第几条路 

void edge(int a, int b, int c) //链式前向星存图 =>从a走到b需要花费c的权值 
{
	//存内容用 
	yy[idx].w = c;
	//遍历用 
	yy[idx].e = b;
	yy[idx].ne = h[a]; //往回遍历的时候该线段的头是上一个点的位置 
	h[a] = idx;  //a的头指向了连接a的线段的位置(idx 
	idx++; //该存下一个点了 
} 

void dijplus(int start, int found)  //从那个点开始遍历,要求到哪个点的最短距离
{
	dist[start] = 0;  //不再是inf
	priority_queue<pii, vector<pii>, greater<pii> > q;  //由小到大排列=>小顶堆
	q.push(make_pair(0,start));
	while(!q.empty()) {
		int now = q.top().second;  //即make_pair(0,start)里start位置的数,0位置是first
		q.pop();
		if(box[now] == 1) {
			continue;
		}
		box[now] = 1;
		for(int i = h[now]; i != -1; i = yy[i].ne) {  //向前的遍历
			int j = yy[i].e;
			dist[j] = min(dist[j], dist[now] + yy[i].w); //更新dist内容
			q.push(make_pair(dist[j], j));  //入堆吧你
		}
	}
	cout<<dist[found];
} 

存在负边

              Bellman-ford  O(nm)

3. Bellman-ford  O(nm)

特征:可有限制边数,即从i到j最多经历k个边的最短距离;可记录负环情况(码中-1),即某一个环的权值和为负数

const int maxn=1e6+10;
const int inf = 0x3f3f3f3f;
int dist[maxn], backup[maxn];
//初始化 
memset(dist, 0x3f, sizeof dist);

//结构体存边
struct st{
	int a, b, w;
}edge[maxn]; 

int bellman_ford()
{
	memset(dist,0x3f,sizeof(dist));
	dist[1] = 0;
	for(int i = 0; i < k; i++) {  //只许用k条边,若无要求则是最少n-1即可 
		memcpy(backup, dist, sizeof(dist)); //backup用来记录上一次的dist状态 
		for(int j = 0; j < m; j++) {      // 枚举所有边
		   int a = edge[j].a, b = edge[j].b, w = edge[j].w;	
		   dist[b] = min(dist[b], backup[a] + w); // 用上一次记录的更新 
		}
	}
	if (dist[n] > 0x3f3f3f3f / 2) {  //判断负环 
		return -1;
	}
	return dist[n];
}

                SPFA  O(m)~O(nm) 

4.SPFA  O(m)~O(nm)

1.用来判断是否存在负环(常用)

链式向前星存图(dijplus里有

//初始化规则
memset(cnt, 0, sizeof cnt);
memset(h, -1, sizeof h);
memset(dist, 0x3f, sizeof dist);
//dist数组的初始化不重要,只需要让判断大小步骤能进入即可证明环是在减小的

bool spfa(int start)
{
	queue<int> q;
	for(int i = 1; i <= n; i++) {
		q.push(i);
	}
	dist[start] = 0;
	box[start] = 1;
	while (q.size()) {
		int now = q.front();
		q.pop();
		box[now] = 0; //
		for (int i = h[now]; i != -1; i = yy[i].ne) {
			int j = yy[i].e;
			if (dist[j] > dist[now] + yy[i].w) { //遇到可以更新的情况 
				dist[j] = dist[now] + yy[i].w;
				cnt[j] = cnt[now] + 1;
				if (cnt[j] >= n) {
					return 1; //存在负环
				}
				if (!box[j]) {
					q.push(j);
					box[j] = 1;
				}
			}
		}
	}
	return 0; //不存在负环
}

2. 求最短路(一般很容易被卡掉,不建议用

//初始化
memset(dist, 0x3f, sizeof dist);

void spfa(int start)
{
	queue<int> q;
	q.push(start);
	dist[start] = 0;
	box[start] = 1;
	while (q.size()) {
		int now = q.front();
		q.pop();
		box[now] = 0;
		for (int i = h[now]; i != -1; i = yy[i].ne) {
			int j = yy[i].e;
			if (dist[j] > dist[now] + yy[i].w) { 
				dist[j] = dist[now] + yy[i].w;
				if (!box[j]) {
					q.push(j);
					box[j] = 1;
				}
			}
		}
	}
}
//内容和判断负环就差了个cnt数组的记录

多源(任意两点互相走)

               floyd(全正边)  O(n^3)        

5. floyd(全正边)  O(n^3) 

 dp且时间复杂度高,但是全面记录了任意点间的最短路

//初始化
memset(dist, 0x3f, sizeof dist);

//存图
for (int i = 1; i <= n; i++) {
    dist[i][i] = 0;
}
dist[a][b] = dist[b][a] = min(dist[a][b], value);  //双向且考虑重边,单向的就省去反向即可


void floyd()
{
    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]);
            }
        }
    }
}

//dist[x][y]即为x点到y点最短路

by yq (有大量参考别人的代码,后续有更深刻的理解再补充

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超高校级のDreamer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值