最短路(常用算法)----更新ing
2017-09-06 16:58
253 查看
模板题目:http://acm.hdu.edu.cn/showproblem.php?pid=2544
n是顶点数,m是边数,参数s是源点,把各个点到源点的最短距离保存在d[maxn]中。
Dijkstra:
思想:
两个集合:未得到最短距离的集合1(初始化1~n)和已得到最短距离集合2(初始化∅)
vis集合==1属于集合2,否则属于集合1,vis【x】 == 1 说明已经计算出x到源点的最短距离
这就决定了这个算法不能运用于负权值的图上,比如有负权值的环,很明显这个环走的次数越多距离越短,然而这个算法不会去找已经去过的点(集合2)
每次从集合1中找离源点最近的点node1,把node1从集合1删掉加到集合2中,然后以这个点为基础查看集合1的点node2是否可以优化:
如果d[node2] < d[node1] + Map[node1][node2]则更新d[node2] = d[node1] + Map[node1][node2]
直到集合1为∅;
函数里面第一个循环是求目前离源点最短距离的点(第一次进循环后 v == s),只有都求出了最短距离,那么v = -1,结束算法
第二个循环是根据上一个循环算出的最小值试着优化其他节点(加一个判断vis[i]是否为1,就可以只更新集合1的元素,但是不影响时间复杂度所以可以不用写)
复杂度O(n^2),只能运用于没有负权值的图(遇到负权值的图就用下面的算法)
用堆优化的Dijkstra:复杂度ElogV
主要优化了找最小距离和松弛操作
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define maxn 105
using namespace std;
typedef pair<int, int> P;//pair的好处是不用重载小于,可以直接比大小
const int inf = 1000000000;
int n, m;
int head[maxn];
bool vis[maxn];
int d[maxn];
struct edge
{
int to;
int cost;
};
vector<edge> M[maxn];
void dijkstra(int s = 1)
{
for(int i = 1; i <= n; i++)
{
d[i] = inf;
vis[i] = 0;
}
d[s] = 0;
priority_queue<P, vector<P>, greater<P>> PQ;
PQ.push(make_pair(0, s));
while(!PQ.empty())
{
P cur = PQ.top(); PQ.pop();
int v = cur.second;
if(vis[v]) continue;
vis[v] = 1;
for(int i = 0; i < M[v].size(); i++)
{
edge e = M[v][i];
int u = e.to;
int cost = e.cost;
if(d[u] > d[v] + cost)
{
d[u] = d[v] + cost;
PQ.push(make_pair(d[u], u));
}
}
}
cout << d
<< endl;
}
int main()
{
while(cin >> n >> m, n + m)
{
memset(head, -1, sizeof(head));
for(int i = 0; i < maxn; i++)
M[i].clear();
for(int i = 0; i < 2 * m; i += 2)
{
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
edge tmp;
tmp.to = v;
tmp.cost = c;
M[u].push_back(tmp);
tmp.to = u;
tmp.cost = c;
M[v].push_back(tmp);
}
dijkstra();
}
return 0;
}
Bellman-Ford:
思路:因为有:d[i] = min(d[i], d[j] + cost(j, i))或者写成d[i] = min(d[j] + cost(j, i)) -->> 假设存在j到i的边
我们遍历每条边进行松弛操作(d[i] = min(d[j] + cost(j, i)) ,松弛成功的话,则s到i就经过(j,i)这条边),因为后续更新可能导致现在的d[j]发生变化,所以要一直更新到没有边松弛成功
补充:
什么时候肯定会结束,或者说能不能结束?
因为最短路径肯定是个简单路径,不可能包含回路的,如果包含回路,且回路的权值和为正的,那么去掉这个回路,可以得到更短的路径
如果回路的权值是负的,明显无解
图有n个点,又不能有回路,所以最短路径最多n-1边,所以最多松弛n-1次(有解情况下),如果第n次还有边可以松弛说明存在负权值的环,无解
所以在有负权值的图上求最短路时,可以记录while循环了多少次,当第n次update还为true时,说明有负环。
复杂度:O(n * m)
可以参考这个
求解所有两点间的最短路
有dp状态转移方程 d[k][i][j] = min(d[k - 1][i][k] + d[k - 1][k][j]),
其中k表示用1-k个点和i,j两点的情况下i到j的最短距离
用二维数组就行
O(n^3),稳定,可以处理负权边(检查是否存在d[i][i]为负数,因为如果没有负圈的话d[i][i]明显等于0)。
高级搜索最短李待补充。
n是顶点数,m是边数,参数s是源点,把各个点到源点的最短距离保存在d[maxn]中。
Dijkstra:
思想:
两个集合:未得到最短距离的集合1(初始化1~n)和已得到最短距离集合2(初始化∅)
vis集合==1属于集合2,否则属于集合1,vis【x】 == 1 说明已经计算出x到源点的最短距离
这就决定了这个算法不能运用于负权值的图上,比如有负权值的环,很明显这个环走的次数越多距离越短,然而这个算法不会去找已经去过的点(集合2)
每次从集合1中找离源点最近的点node1,把node1从集合1删掉加到集合2中,然后以这个点为基础查看集合1的点node2是否可以优化:
如果d[node2] < d[node1] + Map[node1][node2]则更新d[node2] = d[node1] + Map[node1][node2]
直到集合1为∅;
函数里面第一个循环是求目前离源点最短距离的点(第一次进循环后 v == s),只有都求出了最短距离,那么v = -1,结束算法
第二个循环是根据上一个循环算出的最小值试着优化其他节点(加一个判断vis[i]是否为1,就可以只更新集合1的元素,但是不影响时间复杂度所以可以不用写)
复杂度O(n^2),只能运用于没有负权值的图(遇到负权值的图就用下面的算法)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 105 #define inf 100000 using namespace std; int d[maxn]; int s, n, m; int Map[maxn][maxn]; bool vis[maxn]; void pre() { memset(vis, 0, sizeof(vis)); for(int i = 0; i < maxn; i++) { for(int j = 0; j < maxn; j++) { Map[i][j] = inf; } d[i] = inf; } } void Dijkstra(int s = 1) { d[s] = 0; while(true) { int v = -1; for(int i = 1; i <= n; i++) { if(!vis[i] && (v == -1 || d[i] < d[v])) { v = i; } } if(v == -1) break; vis[v] = 1; for(int i = 1; i <= n; i++) { d[i] = min(d[i], d[v] + Map[v][i]); } } } int main() { while(~scanf("%d %d", &n, &m), n && m) { pre(); for(int i = 0; i < m; i ++) { int s, e, cost; scanf("%d %d %d", &s, &e, &cost); Map[s][e] = Map[e][s] = cost; } Dijkstra(); cout << d << endl; } return 0; }
用堆优化的Dijkstra:复杂度ElogV
主要优化了找最小距离和松弛操作
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define maxn 105
using namespace std;
typedef pair<int, int> P;//pair的好处是不用重载小于,可以直接比大小
const int inf = 1000000000;
int n, m;
int head[maxn];
bool vis[maxn];
int d[maxn];
struct edge
{
int to;
int cost;
};
vector<edge> M[maxn];
void dijkstra(int s = 1)
{
for(int i = 1; i <= n; i++)
{
d[i] = inf;
vis[i] = 0;
}
d[s] = 0;
priority_queue<P, vector<P>, greater<P>> PQ;
PQ.push(make_pair(0, s));
while(!PQ.empty())
{
P cur = PQ.top(); PQ.pop();
int v = cur.second;
if(vis[v]) continue;
vis[v] = 1;
for(int i = 0; i < M[v].size(); i++)
{
edge e = M[v][i];
int u = e.to;
int cost = e.cost;
if(d[u] > d[v] + cost)
{
d[u] = d[v] + cost;
PQ.push(make_pair(d[u], u));
}
}
}
cout << d
<< endl;
}
int main()
{
while(cin >> n >> m, n + m)
{
memset(head, -1, sizeof(head));
for(int i = 0; i < maxn; i++)
M[i].clear();
for(int i = 0; i < 2 * m; i += 2)
{
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
edge tmp;
tmp.to = v;
tmp.cost = c;
M[u].push_back(tmp);
tmp.to = u;
tmp.cost = c;
M[v].push_back(tmp);
}
dijkstra();
}
return 0;
}
Bellman-Ford:
思路:因为有:d[i] = min(d[i], d[j] + cost(j, i))或者写成d[i] = min(d[j] + cost(j, i)) -->> 假设存在j到i的边
我们遍历每条边进行松弛操作(d[i] = min(d[j] + cost(j, i)) ,松弛成功的话,则s到i就经过(j,i)这条边),因为后续更新可能导致现在的d[j]发生变化,所以要一直更新到没有边松弛成功
补充:
什么时候肯定会结束,或者说能不能结束?
因为最短路径肯定是个简单路径,不可能包含回路的,如果包含回路,且回路的权值和为正的,那么去掉这个回路,可以得到更短的路径
如果回路的权值是负的,明显无解
图有n个点,又不能有回路,所以最短路径最多n-1边,所以最多松弛n-1次(有解情况下),如果第n次还有边可以松弛说明存在负权值的环,无解
所以在有负权值的图上求最短路时,可以记录while循环了多少次,当第n次update还为true时,说明有负环。
复杂度:O(n * m)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 105 #define maxm 10005 #define inf 100000 using namespace std; int n, m; int d[maxn]; struct edge { int s; int e; int cost; }node[maxm]; void pre() { fill(d, d + maxn, inf); } void Bellman_Ford(int s = 1) { d[s] = 0; while(true) { bool update = false; for(int i = 0; i < m; i++) { edge E = node[i]; if(d[E.s] != inf && d[E.e] > d[E.s] + E.cost) { d[E.e] = d[E.s] + E.cost; update = true; } } if(!update) break; } } int main() { while(~scanf("%d %d", &n, &m), n && m) { pre(); m <<= 1; for(int i = 0; i < m; i += 2) { int s, e, cost; scanf("%d %d %d", &s, &e, &cost); node[i].s = s; node[i].e = e; node[i].cost = cost; node[i + 1].e = s; node[i + 1].s = e; node[i + 1].cost = cost; } Bellman_Ford(); cout << d << endl; } return 0; }SPFA(Bellman-Ford的队列实现 ):
可以参考这个
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxn 105 #define maxm 10005 #define inf 100000 using namespace std; int n, m; int d[maxn]; int Map[maxn][maxn]; bool vis[maxn]; void pre(int s = 1) { fill(d, d + maxn, inf); fill(vis, vis + maxn, false); for(int i = 0; i < maxn; i++) { for(int j = 0; j < maxn; j++) { Map[i][j] = inf; } } d[s] = 0; } void SPFA(int s = 1) { queue<int> q; q.push(s); vis[s] = 1; while(!q.empty()) { int pos = q.front(); q.pop(); vis[pos] = false; for(int i = 1; i <= n; i++) { if(d[pos] + Map[pos][i] < d[i]) { d[i] = d[pos] + Map[pos][i]; if(!vis[i]) { q.push(i); vis[i] = true; } } } } } int main() { while(~scanf("%d %d", &n, &m), n && m) { pre(); for(int i = 0; i < m; i++) { int s, e, cost; scanf("%d %d %d", &s, &e, &cost); Map[s][e] = Map[e][s] = cost; } SPFA(); cout << d << endl; } return 0; }Floyd-Wallshall
求解所有两点间的最短路
有dp状态转移方程 d[k][i][j] = min(d[k - 1][i][k] + d[k - 1][k][j]),
其中k表示用1-k个点和i,j两点的情况下i到j的最短距离
用二维数组就行
O(n^3),稳定,可以处理负权边(检查是否存在d[i][i]为负数,因为如果没有负圈的话d[i][i]明显等于0)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define maxn 105 #define maxm 10005 #define inf 100000 using namespace std; int n, m; int d[maxn][maxn]; void pre(int s = 1) { for(int i = 0; i < maxn; i++) { for(int j = 0; j < maxn; j++) { d[i][j] = inf; } d[i][i] = 0; } } void Floyd_Wallshall(int s = 1) { for(int k = 1; k <= n; k++) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } } } int main() { while(~scanf("%d %d", &n, &m), n && m) { pre(); for(int i = 0; i < m; i++) { int s, e, cost; scanf("%d %d %d", &s, &e, &cost); d[s][e] = d[e][s] = cost; } Floyd_Wallshall(); cout << d[1] << endl; } return 0; }
高级搜索最短李待补充。
相关文章推荐
- ORACLE 常用技巧(持续更新ing)
- 最短路 - spfa - (二) 【 理解 + 例题 】 更新 ing......
- ASP.NET常用的2种导出EXCEL方式~(更新ING)
- Android Studio 常用技巧(持续更新ing)
- Exchange 常用命令(不断更新ing...)
- Vertica的这些事<五>—— 关于vertica常用函数介绍(持续更新ing)
- 编程常用电子书(不断更新ing...)
- Cisco技术区常用配置实例整理----2010.11.1更新ing....
- linux /os x常用指令(持续更新ing。。)
- 常用的奇妙的小算法--不断更新中
- ACM 中常用小算法(慢慢更新)
- 若干数据结构 && 算法面试题【四】(更新ing)
- MaxCompute常用语句汇总(更新ing)
- 最短路 - floyd 【 理解 + 例题 】 更新 ing...
- STL中常用的一些算法函数[持续更新]
- MaxCompute常用语句汇总(更新ing)
- linux常用命令(更新ing......)
- 最短路的几种常用算法
- 常用自写函数[更新ing]
- 常见面试算法小题目分享~更新ing~