Book--追溯 最短路径各类算法及优化

本文总结了多种经典的最短路径算法,包括Bellman-Ford、Dijkstra及其堆优化版本、Floyd以及SPFA算法。详细介绍了每种算法的实现思路、应用场景及复杂度,并提供了相应的代码实现。

2015-01-04 16:54:53

以Hdu 2544 为测试平台,浅结回顾最短路各类算法。

First:Bellman-Ford(边权可为负,可找负圈,复杂度:O(V*E))

  思路回顾:如果不存在负圈,那么最短路不会经过一个点两次,那么最短路长度<= V-1,对全图进行V-1次松弛,每次松弛检查每条边,如果dis[to] > dis[from] + cost则更新。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 typedef pair<int,int> pii;
20 const int INF = (1 << 30) - 1;
21 const int maxn = 110;
22 
23 int N,M,ecnt;
24 int dis[maxn];
25 
26 struct edge{
27     int u,v,cost;
28 }e[maxn * maxn];
29 
30 int Bellman_ford(int s){
31     for(int i = 1; i <= N; ++i)
32         dis[i] = INF;
33     dis[s] = 0;
34     for(int k = 1; k < N; ++k){
35         bool flag = false;
36         for(int i = 1; i <= ecnt; ++i){
37             int u = e[i].u;
38             int v = e[i].v;
39             if(dis[u] != INF && dis[v] > dis[u] + e[i].cost){
40                 dis[v] = dis[u] + e[i].cost;
41                 flag = true;
42             }
43         }
44         if(!flag) break;
45     }
46     return dis[N];
47 }
48 
49 void Add_edge(int u,int v,int c){
50     e[++ecnt].u = u;
51     e[ecnt].v = v;
52     e[ecnt].cost = c;
53 
54     e[++ecnt].u = v;
55     e[ecnt].v = u;
56     e[ecnt].cost = c;
57 }
58 
59 int main(){
60     int a,b,c;
61     while(scanf("%d%d",&N,&M) != EOF){
62         if(N == 0 && M == 0) break;
63         ecnt = 0;
64         for(int i = 1; i <= M; ++i){
65             scanf("%d%d%d",&a,&b,&c);
66             Add_edge(a,b,c);
67             Add_edge(b,a,c);
68         }
69         printf("%d\n",Bellman_ford(1));
70     }
71     return 0;
72 }
View Code

Second:Dijstra(边权须为正,复杂度:O(V*V),堆优化:O((E+V)logV)

  思路回顾:BF算法中显然存在很多多余的检查,Dij算法是从一个只包含起点的点集开始,不断找出距离点集外距离起点最近的点,并加入点集,同时用新加入的点对dis[]进行松弛。

(1)朴素Dijstra

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 typedef pair<int,int> pii;
20 const int INF = (1 << 30) - 1;
21 const int maxn = 110;
22 
23 int N,M;
24 int first[maxn],next[maxn * maxn],ver[maxn * maxn],ecnt;
25 int dis[maxn],used[maxn];
26 struct edge{
27     int v,cost;
28 }e[maxn * maxn];
29 
30 void Add_edge(int u,int v,int c){
31     next[++ecnt] = first[u];
32     e[ecnt].v = v;
33     e[ecnt].cost = c;
34     first[u] = ecnt;
35 }
36 
37 int Dijstra(int s){
38     memset(used,0,sizeof(used));
39     fill(dis + 1,dis + N + 1,INF);
40     dis[s] = 0;
41     int p;
42     for(int k = 1; k <= N; ++k){
43         int tmin = INF;
44         for(int i = 1; i <= N; ++i)
45             if(!used[i] && dis[i] < tmin) tmin = dis[p = i];
46         used[p] = 1;
47         for(int i = first[p]; i != -1; i = next[i]){
48             int v = e[i].v;
49             if(!used[v] && dis[v] > dis[p] + e[i].cost)
50                 dis[v] = dis[p] + e[i].cost;
51         }
52     }
53     return dis[N];
54 }
55 
56 
57 int main(){
58     int a,b,c;
59     while(scanf("%d%d",&N,&M) != EOF){
60         if(N == 0 && M == 0) break;
61         memset(first,-1,sizeof(first));
62         ecnt = 0;
63         for(int i = 1; i <= M; ++i){
64             scanf("%d%d%d",&a,&b,&c);
65             Add_edge(a,b,c);
66             Add_edge(b,a,c);
67         }
68         printf("%d\n",Dijstra(1));
69     }
70     return 0;
71 }
View Code

(2)堆(优先队列)优化Dijstra

  细节:注意Dij里面的优先队列里面存的是dis[]数组的元素,即起点到某点的距离,与prim的堆中存边是不一样的。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
#define lp (p << 1)
#define rp (p << 1|1)
#define getmid(l,r) (l + (r - l) / 2)
#define MP(a,b) make_pair(a,b)
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int INF = (1 << 30) - 1;
const int maxn = 110;

int N,M;
int first[maxn],nxt[maxn * maxn],ver[maxn * maxn],ecnt;
int dis[maxn];
struct edge{
    int v,cost;
    friend bool operator < (edge a,edge b){
        return a.cost > b.cost;
    }
}e[maxn * maxn];

void Add_edge(int u,int v,int c){
    nxt[++ecnt] = first[u];
    e[ecnt].v = v;
    e[ecnt].cost = c;
    first[u] = ecnt;
}

struct cmp{
    bool operator ()(pii a,pii b){
        return a.first > b.first;
    }
};

int Dijstra(int s){
    priority_queue<pii,vector<pii >,cmp> PQ;
    fill(dis + 1,dis + N + 1,INF);
    dis[s] = 0;
    PQ.push(MP(dis[s],s));
    int cnt = 0;
    while(!PQ.empty()){
        pii x = PQ.top(); PQ.pop();
        if(dis[x.second] < x.first) continue; //当前的x并非最短路径,舍弃
        for(int i = first[x.second]; i != -1; i = nxt[i]){
            int v = e[i].v;
            if(dis[v] > dis[x.second] + e[i].cost){
                dis[v] = dis[x.second] + e[i].cost;
                PQ.push(MP(dis[v],v));
            }
        }
    }
    return dis[N];
}

int main(){
    int a,b,c;
    while(scanf("%d%d",&N,&M) != EOF){
        if(N == 0 && M == 0) break;
        memset(first,-1,sizeof(first));
        ecnt = 0;
        for(int i = 1; i <= M; ++i){
            scanf("%d%d%d",&a,&b,&c);
            Add_edge(a,b,c);
            Add_edge(b,a,c);
        }
        printf("%d\n",Dijstra(1));
    }
    return 0;
}
View Code

 

Third:Floyd(边权可为负,可判是否有负圈,复杂度:O(V*V*V)

  思路回顾:全图V次松弛

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 typedef pair<int,int> pii;
20 const int INF = (1 << 30) - 1;
21 const int maxn = 110;
22 
23 int N,M;
24 int g[maxn][maxn];
25 
26 void Floyd(){
27     for(int k = 1; k <= N; ++k)
28         for(int i = 1; i <= N; ++i)
29             for(int j = 1; j <= N; ++j)
30                 g[i][j] = min(g[i][j],g[i][k] + g[k][j]);
31 }
32 
33 int main(){
34     int a,b,c;
35     while(scanf("%d%d",&N,&M) != EOF){
36         if(N == 0 && M == 0) break;
37         for(int i = 1; i <= N; ++i)
38             fill(g[i] + 1,g[i] + N + 1,INF);
39         for(int i = 1; i <= M; ++i){
40             scanf("%d%d%d",&a,&b,&c);
41             g[a][b] = g[b][a] = min(g[a][b],c);
42         }
43         Floyd();
44         printf("%d\n",g[1][N]);
45     }
46     return 0;
47 }
View Code

Fourth:SPFA(边权可为负,可判是否有负圈,复杂度:O(E),关于SPFA的研究讨论,推荐这篇论文:http://wenku.baidu.com/view/f22d0d36ee06eff9aef807e9.html

  思路回顾:队列优化Bellman-Ford,建立队列,初始先将起点入队,然后不断地取队首点,并且用该点的邻边去松弛,将能起到松弛作用边的另一点加入队列(当该点还不在队列中时),可以发现dis[]会不断变小,直至队列为空。

  BFS版SPFA判负环:可以知道若不存在负环,那么一个点最多入队V次(根据度数)。那么若某点入队超过V次,则有负环。

  DFS版SPFA判负环:根据最短路原理,若一个点入队多次,则有负环。判负环速度比BFS版快?倍!

BFS版:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <vector>
 6 #include <map>
 7 #include <set>
 8 #include <stack>
 9 #include <queue>
10 #include <iostream>
11 #include <algorithm>
12 using namespace std;
13 #define lp (p << 1)
14 #define rp (p << 1|1)
15 #define getmid(l,r) (l + (r - l) / 2)
16 #define MP(a,b) make_pair(a,b)
17 typedef long long ll;
18 typedef unsigned long long ull;
19 typedef pair<int,int> pii;
20 const int INF = (1 << 30) - 1;
21 const int maxn = 110;
22 
23 int N,M;
24 int first[maxn],next[maxn * maxn],ver[maxn * maxn],ecnt;
25 int inq[maxn],dis[maxn];
26 
27 struct edge{
28     int v,cost;
29 }e[maxn * maxn];
30 
31 void Add_edge(int u,int v,int c){
32     next[++ecnt] = first[u];
33     e[ecnt].v = v;
34     e[ecnt].cost = c;
35     first[u] = ecnt;
36 }
37 
38 int Spfa(int s){
39     queue<int> Q;
40     memset(inq,0,sizeof(inq));
41     fill(dis + 1,dis + N + 1,INF);
42     Q.push(s);
43     inq[s] = 1;
44     dis[s] = 0;
45     while(!Q.empty()){
46         int x = Q.front(); Q.pop();
47         inq[x] = 0;
48         for(int i = first[x]; i != -1; i = next[i]){
49             int v = e[i].v;
50             if(dis[v] > dis[x] + e[i].cost){
51                 dis[v] = dis[x] + e[i].cost;
52                 if(inq[v] == 0){
53                     inq[v] = 1;
54                     Q.push(v);
55                 }
56             }
57         }
58     }
59     return dis[N];
60 }
61 
62 int main(){
63     int a,b,c;
64     while(scanf("%d%d",&N,&M) != EOF){
65         if(N == 0 && M == 0) break;
66         memset(first,-1,sizeof(first));
67         ecnt = 0;
68         for(int i = 1; i <= M; ++i){
69             scanf("%d%d%d",&a,&b,&c);
70             Add_edge(a,b,c);
71             Add_edge(b,a,c);
72         }
73         printf("%d\n",Spfa(1));
74     }
75     return 0;
76 }
View Code

DFS版:

  由于DFS实测最短路超时orz... 因此DFS版主要用于判负环存在性。

 

转载于:https://www.cnblogs.com/naturepengchen/articles/4202547.html

豌豆代理(又称豌豆 IP)是一款一站式国内代理 IP 服务平台,主打高匿名、低延迟、高可用的 IP 资源,支持 HTTP/HTTPS/SOCKS5 协议,适配 Windows、Mac、Android、iOS 多平台。 多类型 IP 资源与高覆盖节点 提供动态住宅 IP、静态独享 IP、数据中心 IP,覆盖全国 200 + 城市,可用率 99%+;支持省市精准选择或全国混拨,适配不同业务合规与稳定性需求。 使用:在客户端 “节点 / 线路” 页,按城市 / 类型筛选,一键连接目标 IP,适合爬虫、电商多账号运营等场景。 秒级 IP 切换与灵活调度 支持手动一键切换、秒级动态切换(切换速度低至 100ms)、定时切换(自定义时长),并自动过滤重复 IP,避免重复使用导致风险。 使用:在 “设置” 中开启 “自动切换” 并设时间间隔,或按 Ctrl+Q 快捷键一键换 IP,适配反爬虫、批量测试等高频切换场景。 全协议支持与多端适配 兼容 HTTP/HTTPS/SOCKS5 主流代理协议,可对接浏览器、爬虫脚本、客户端软件;支持 Windows、Mac、安卓、iOS 多端同步使用,跨设备无缝切换。 使用:在客户端 “协议设置” 选择对应协议,生成代理地址与端口,直接填入目标软件即可生效。 隐私安全与数据加密 自研传输加密技术保护数据传输,搭配高匿名 IP 隐藏真实地址,同时支持自动清除 Cookie / 缓存,降低隐私泄露与追踪风险。 使用:在 “安全设置” 中开启 “数据加密” 与 “自动清理缓存”,公共 WiFi 环境下优先启用,提升隐私防护等级。 智能筛选与稳定网络优化 系统自动筛选低延迟、高可用 IP,过滤失效 / 重复地址;依托自建纯净机房与独享带宽,搭配 BGP 多线接入,保障连接稳定性与速度。 使用:无需手动配置,客户端默认智能匹配合适节点,复杂网络环境可在 “网络
在网络高速发展的时代,众多的软件被开发出来,给用户带来了很大的选择余地,而且人们越来越追求更个性的需求。在这种时代背景下,商家只能以用户为导向,以商品的持续创新作为商家最重要的事项。 在新发展的时代,人们对幼儿资源互助共享平台越来越重视,才能实现幼儿资源互助共享平台的有效发挥,本文将通过幼儿资源互助共享平台的信息,分析在日常生活中对幼儿资源互助共享平台存在哪些问题探讨出进一步提升效率,管理能力的对策。 系统采用了Java技术,将所有模块采用以浏览器交互的模式,选择MySQL作为系统的数据库,来进行系统的设计。基本实现了幼儿资源互助共享平台应有的主要功能模块,本系统有管理员:首页、个人中心、用户管理、卖家管理、咨询师管理、萌宝信息管理、幼儿知识管理、保姆推荐管理、音频资源管理、二手商品管理、商品分类管理、资源分类管理、交流论坛、系统管理,用户;首页、个人中心、萌宝信息管理、保姆推荐管理、音频资源管理,卖家;首页、个人中心、二手商品管理、订单管理,咨询师;首页、个人中心、幼儿知识管理,前台首页;首页、萌宝信息、幼儿知识、保姆推荐、音频资源、二手商品、交流论坛、个人中心、后台管理、购物车等功能。 对系统进行测试后,改善了程序逻辑和代码。同时确保系统中所有的程序都能正常运行,所有的功能都能操作,本系统的开发获取幼儿资源互助共享平台信息能够更加方便快捷,同时也使幼儿资源互助共享平台信息变的更加系统化、有序化。系统界面较友好,易于操作。
创新!高级!【日前、日内非滚动、日内滚动调度以及实时修正】考虑需求侧响应的智慧楼宇多时间尺度调度策略(Matlab代码实现)内容概要:本文介绍了名为“创新!高级!【日前、日内非滚动、日内滚动调度以及实时修正】考虑需求侧响应的智慧楼宇多时间尺度调度策略”的Matlab代码实现资源,重点围绕智慧楼宇在多时间尺度下的能量调度优化展开,涵盖日前调度、日内非滚动调度、日内滚动调度及实时修正四个阶段,并充分考虑需求侧响应机制,旨在提升能源利用效率与系统运行经济性。该资源提供了完整的Matlab代码实现,适用于科研复现与工程仿真,属于电力系统与智能建筑交叉领域的前沿研究内容。; 适合人群:具备一定电力系统背景、能源管理或自动化专业知识,熟悉Matlab编程,从事科研、教学或工程应用的研究生、高校教师及研发人员;尤其适合开展综合能源系统、需求响应、微电网调度等相关课题的研究者。; 使用场景及目标:①用于智慧楼宇多时间尺度优化调度模型的搭建与仿真;②支持考虑需求侧响应的电能管理策略研究;③服务于学术论文复现、毕业设计或科研项目开发,帮助理解多阶段优化架构与Matlab求解器应用。; 阅读建议:建议结合电力系统优化理论与Matlab编程实践同步学习,重点关注各调度阶段的数学建模思路与代码实现逻辑,推荐使用YALMIP+CPLEX等工具包进行求解,同时参考文中提及的网盘资料获取完整代码与案例数据以提升学习效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值