• 被某人咕咕咕了两个月的最短路(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的博客

    下次一定

    夜空中最亮的星,请照亮我前行
  • 相关阅读:
    周总结(第十一周)
    周总结(第十周)
    周总结(第九周)
    周总结(第八周)
    周总结(第七周)
    周总结(第六周)
    周总结(第5周)
    周总结(第四周)
    周记
    补周记
  • 原文地址:https://www.cnblogs.com/Nefelibata/p/13909609.html
Copyright © 2020-2023  润新知