被某人咕咕咕了两个月的最短路(floyd && dijkstra)

前言

最短路需要用到图论的知识
图论基础知识@jmy orz

这篇博客本一直处于咕咕咕的状态,但由于某人最短路的突击检查并不理想,因此写一篇博客进行细致(才怪)的总结。

Floyd

什么是Floyd呢?问问度娘吧

Floyd 和 区间DP 有点像,动态规划点集大小为区间。
在突测中,本人非常zz的把k循环打错位置了,却忽略了dp[i][j][k]表示的是由i到j途径<= k 的点时的最短路

代码
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;

int n, m;
int dp[105][105];
int pre[105][105];
int main() {
	scanf("%d %d", &n, &m);
	int x = 0, y = 0;
	memset(dp, 0x3f, sizeof(dp));
	for(int i = 1; i<= n; i ++){
		dp[i][i] = 0;
	}
	int m;
	for(int i = 1; i <= m; i ++){
		int x, y;
		scanf("%d %d", &x, &y);
		dp[x][y] = 1;
 	}
	for(int k = 1; k <= n; k ++){
		for(int i = 1; i <= n; i ++){
			for(int j = 1; j <= n; j ++){
				if((dp[i][k] + dp[k][j]) < dp[i][j]){
					dp[i][j] = dp[i][k] + dp[k][j];
				}
			}
		}
	}
	int s, t;
	scanf("%d %d", &s, &t);
	if(dp[s][t] = 0x3f){
		printf("no solution
");
	}
	else{
		printf("%d", dp[s][t]);
	}
	return 0;
} 
题目描述

有 n 个 城市,从1到n给他们编号,它们之间由一些单向道路(即一条道路只能从一个方向走向另一个方向,反之不行)相连,每条路还有一个花费c(i),表示通过第i条边需要花费c(i)的时间。

求任意两点间的最快路径

输入格式

第一行一个整数,表示有多少个城市和多少条道路。

接下来n行,每行n个整数

第i + 1行第j个数表示从到有一条花费为的边。(第行第个数为0)

输出格式

n行,每行n个整数

第i行第j个数表示从到最少需要多少时间。(第行第个数为0)

样例
输入样例

4
0 487 569 408
705 0 306 357
95 222 0 618
961 401 688 0

输出数据

0 487 569 408
401 0 306 357
95 222 0 503
783 401 688 0

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 505;

int n;
int dp[maxn][maxn];
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            dp[i][j] = 0x3f3f3f3f;
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &dp[i][j]);
        }
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            int j = i + k - 1;
            for (int j = 1; j <= n; j++) {
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            printf("%d ", dp[i][j]);
        }
        printf("
");
    }
    return 0;
}

从上述代码中我们就可以显而易见的发现,Floyd有一个致命的弱点就是它的时间复杂度(O(n^3)) 但是它也有很多优点呀!比如它可以实现负权的情况,同时它可以多源点查询

但我们要输出最短路径该怎么做呢?

用递归实现就好了,每一位保存其对应的前一位的位置

#include <cstdio>
#include <cstring>
using namespace std;
int n, m;
int dp[505][505];
int pre[505][505];
int flag1, flag2;
void print(int x){
	if(pre[flag1][x] == 0){
		return;
	}
	print(pre[flag1][x]);
	printf(" %d", x);
}
int main() {
	memset(dp, 0x3f, sizeof(dp));
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i ++){
		dp[i][i] = 0;
	}
	for(int i = 1; i <= m; i ++){
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		dp[a][b] = c;
		pre[a][b] = a;
	}
	for(int k = 1; k <= n; k ++){
		for(int i = 1; i <= n; i ++){
			for(int j = 1; j <= n; j ++){
				if(dp[i][j] > dp[i][k] + dp[k][j]){
					dp[i][j] = dp[i][k] + dp[k][j];
					pre[i][j] = pre[k][j];
				}
			}
		}
	}
	scanf("%d %d", &flag1, &flag2);
	printf("%d
", dp[flag1][flag2]);
	printf("%d", flag1);
	print(flag2);
	return 0;
}

Dijkstra

有啥不会问度娘

思想

运用贪心的思想,不断的找出未被标记且离s最近的点,并运用松弛的思想,改变运用该点松弛的值,在遍历完成后,输出结果。

证明(非严格,不详细)

因为每一个输出时, 它已经成为了未被标记且离s最近的点,在剩下的其余点中,无论如何都不可能短于它,也就不可能构成其的最短路,而在前面已被输出的数中能连接的都进行了判断,不能连接的。。。
所以当每个点都被标注时都成为了其最短路。(我哔哔了些啥???)

堆优化版本代码(我的代码是真的

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 2505;

struct node{
	int v, w;
};

struct data{
	int p, vis;
	bool operator < (const data x) const {return vis > x.vis;} 
};

int n, m, s, t;
int vis[maxn];
int v[maxn];
vector<node> way[maxn];
priority_queue<data> q;

void dijkstra(int s, int t){
	memset(vis, 0x3f3f3f3f, sizeof(vis));
	memset(v, 0, sizeof(v));
	vis[s] = 0;
	data flag2;
	flag2.p = s;
	flag2.vis = 0;
	q.push(flag2);
	while(!q.empty()){
		data flag3;
		flag3 = q.top();        // flag3 为当前最小值 
		q.pop();
		if(v[flag3.p] == 1)continue;
		v[flag3.p] = 1;
		for(int i = 0; i < way[flag3.p].size(); i ++){
			node flag4;
			flag4 = way[flag3.p][i];
			if(vis[flag4.v] > way[flag3.p][i].w + vis[flag3.p]){
				vis[flag4.v] = way[flag3.p][i].w + vis[flag3.p];
				data flag5;
				flag5.p = flag4.v;
				flag5.vis = vis[flag4.v];
				q.push(flag5);
			}
		}
	}
}
int main() {
	scanf("%d %d %d %d", &n, &m, &s, &t);
	for(int i = 1; i <= m; i ++){
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		node flag;
		flag.v = b;
		flag.w = c;
		way[a].push_back(flag);
		flag.v = a;
		way[b].push_back(flag);
	}
	dijkstra(s, t);
	printf("%d", vis[t]);
	return 0;
}

之后应该还会有Bellman_fort, spfa的博客

下次一定

夜空中最亮的星,请照亮我前行
原文地址:https://www.cnblogs.com/Nefelibata/p/13909609.html