POJ-2387.Til the Cows Come Home.(五种方法:Dijkstra + Dijkstra堆优化 + Bellman-Ford + SPFA + Floyd-Warshall)
昨天刚学习完最短路的算法,今天开始练题发现我是真的菜呀,居然能忘记邻接表是怎么写的,真的是菜的真实......
为了弥补自己的菜,我决定这道题我就要用五种办法写出,并在Dijkstra算法堆优化中另外给出邻接表存储实现的操作,唉,真是令人窒息......
言归正传吧,毕竟我这么菜,也不会讲什么大道理......
呜哇呜哇.jpg
原题链接
本题大意:给定n结点,a和b表示其中的两个结点,输出t组a和b和w表示a和b距离w可以互相抵达,求从n走到1的最短路径...
本题思路:建图之后直接单源最短路任意结点之间的最短路随便解,只是有些算法会超时,我这里仅是作为练习算法去练习其他算法,读者可自行尝试......
先给出解决无负权单源最短路的最优算法Dijkstra算法吧,包括其优化:
Dijkstra :我再叙述一下这个算法的作用机理吧,将整个图的结点分为两个部分,一部分为已经确定最短路径的集合S,另一部分当然是n - S, 初始状态下S内不包含任何结点。接着在n - S中挑选一个权值最小的结点,将该结点直接
放进S中表示改结点到源结点的最短路径求解完毕,在n - S中对所有与该结点有公共边的结点做松弛操作,松弛的大体意思就是通过已知的最短路来求解与他相连的边的结点的最短路......
为啥上面我们要在n - S中寻找结点做松弛操作呢,因为S中的结点已经被求出了最短路,那为什么每次找的那个最小值就保证它已经是最短路了呢?这......证明简直花了算法导论一大堆篇章,我看懂了但我不说emm...(其实还是不懂...
小声bb.jpg)
参考代码:
好了,我们要开始写代码了
首先给出没有用堆优化的Dijkstra算法:
//Dijkstra未优化版 : 47ms
#include <iostream>
#include <cstring>
using namespace std; const int maxn = 1e3 + , INF = 0x3f3f3f3f;
int t, n, k, dist[maxn], G[maxn][maxn];
bool vis[maxn]; int Dijkstra(int source) {
for(int i = ; i <= n; i ++)
dist[i] = (i == source ? : INF);
for(int i = ; i <= n; i ++) {
int Min = INF;
for(int j = ; j <= n; j ++)
if(!vis[j] && dist[j] < Min) {
Min = dist[j];
k = j;
}
vis[k] = true;
for(int j = ; j <= n; j ++) {
if(G[k][j] != INF && !vis[j] && dist[j] > dist[k] + G[k][j])
dist[j] = dist[k] + G[k][j];
}
}
return dist[n];
} int main () {
cin >> t >> n;
int a, b, w;
memset(vis, false, sizeof vis);
for(int i = ; i <= n; i ++)
for(int j = ; j <= n; j ++)
G[i][j] = INF;
for(int i = ; i < t; i ++) {
cin >> a >> b >> w;
G[a][b] = min(G[a][b], w);
G[b][a] = min(G[b][a], w);
}
cout << Dijkstra() << endl;
return ;
}
唉,烦人的Vj,每次给的Test time都不一样......
邻接矩阵存储的堆优化的Dijkstra算法:
//堆优化的Dijkstra算法: 141ms
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std; typedef pair<int, int> Pii;
const int maxn = + , INF = 0x3f3f3f3f;
int t, n, dist[maxn];
bool vis[maxn];
struct edge {
int to, cost;
};
vector <edge> G[maxn]; void addedge(int u, int v, int w) {
G[u].push_back({v, w});
} int Dijkstra(int source) {
for(int i = ; i <= n; i ++)
dist[i] = (i == source ? : INF);
priority_queue <Pii, vector<Pii>, greater<Pii> > Q;
Q.push(make_pair(, ));
while(!Q.empty()) {
Pii p = Q.top();
Q.pop();
if(vis[p.second]) continue;
vis[p.second] = true;
for(int i = ; i < G[p.second].size(); i ++) {
edge e = G[p.second][i];
if(dist[e.to] > dist[p.second] + e.cost) {
dist[e.to] = dist[p.second] + e.cost;
Q.push(make_pair(dist[e.to], e.to));
}
}
}
return dist[n];
} int main () {
cin >> t >> n;
int a, b, w;
for(int i = ; i < t; i ++) {
cin >> a >> b >> w;
addedge(a, b, w);
addedge(b, a, w);
}
cout << Dijkstra() << endl;
return ;
}
Bellman-Ford:下面叙述一下Bellman-Ford算法的作用机理。
(1)初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
(2)迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
(3)检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
下面我们给出该算法的代码:
// /*Bellman-Ford算法:172ms
#include <iostream>
#include <cstring>
#include <vector>
using namespace std; typedef pair<int, int> Pii;
struct edge {
int to, cost;
};
const int maxn = + , INF = 0x3f3f3f3f;
vector <edge> G[maxn];
int t, n, dist[maxn]; void addedge(int u, int v, int w) {
G[u].push_back({v, w});
} int Bellman_Ford(int source) {
bool flag;
for(int i = ; i <= n; i ++)
dist[i] = (i == source ? : INF);
for(int k = ; k < n; k ++) {
flag = false;
for(int i = ; i <= n; i ++) {
for(int j = ; j < G[i].size(); j ++) {
edge e = G[i][j];
if(dist[e.to] > dist[i] + e.cost) {
dist[e.to] = dist[i] + e.cost;
flag = true;
}
}
}
if(!flag) break;
}
return dist[n];
} int main() {
cin >> t >> n;
int a, b, w;
for(int i = ; i < t; i ++) {
cin >> a >> b >> w;
addedge(a, b, w);
addedge(b, a, w);
}
cout << Bellman_Ford() << endl;
return ;
}
// */
SPFA:这是Bellman-Ford算法的一种队列优化算法,具体是怎么个优化法呢,听我下面详细道来......
SPFA算法全称为Shortest Path Fast Algorithm,在1994年由西南交通大学段凡丁提出,与Bellman-Ford算法一样,用于求解含负权的最短路问题以及判断是否存在负权环。在不含负权环的题情况下优先选择堆优化的Dijkstra算法求最短路径,这就避免SPFA出现最坏的情况。SPFA算法的基本思路与Bellman-Ford算法相同,即每个节点都被用作用于松弛其相邻节点的备选节点。相较于Bellman-Ford算法,SPFA算法的提升在于它并不盲目尝试所有节点,而是维护一个备选节点队列,并且仅有节点被松弛后才会放入队列中。整个流程不断重复直至没有节点可以被松弛。
下面给出这个算法的代码吧:
#include <iostream>
#include <queue>
#include <cstring>
using namespace std; struct edge {
int to, cost;
};
const int maxn = + , INF = 0x3f3f3f3f;
vector <edge> G[maxn];
int t, n, now, dist[maxn];
bool vis[maxn]; void addedge(int u, int v, int w) {
G[u].push_back({v, w});
} int Spfa(int source) {
memset(vis, false, sizeof vis);
for(int i = ; i <= n; i ++)
dist[i] = (i == source ? : INF);
queue <int> Q;
Q.push(source);
vis[source] = true;
while(!Q.empty()) {
now = Q.front();
Q.pop();
for(int i = ; i < G[now].size(); i ++) {
edge e = G[now][i];
if(dist[e.to] > dist[now] + e.cost) {
dist[e.to] = dist[now] + e.cost;
if(!vis[e.to]) Q.push(e.to);
}
}
}
return dist[n];
} int main() {
cin >> t >> n;
int a, b, w;
for(int i = ; i < t; i ++) {
cin >> a >> b >> w;
addedge(a, b, w);
addedge(b, a, w);
}
cout << Spfa() << endl;
return ;
}
Floyd-Warshall:这个算法的本事很大,可以用来求所有结点的最短路,但用此算法求单源最短路也是可以的,就显得比较大材小用,本领强其复杂度必然臃肿......好了下面我介绍一下该算法的基本思路吧。
Floyd-Warshall算法通过逐步改进两个顶点之间的最短路径来实现,直到估计是最优的。
是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包[2]。
Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
原理:Floyd-Warshall算法的原理是动态规划。
设D(i, j, k)为从i到j的只以(1....k)集合中的节点为中间节点的最短路径的长度。
- 若最短路径经过点k,D(i, j, k) = D(i, k, k - 1) + D(k, j, k - 1);
- 若最短路径不经过点k,则D(i, j, k) = D(i, j, k - 1)。
因此,D(i, j, k) = min(, D(i, j, k - 1), D(i, k, k - 1) + D(k, j, k - 1));。
在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。
参考代码:
// /*Floyd-Warshall算法:TLE,具体时间不详
#include <iostream>
#include <queue>
#include <cstring>
using namespace std; struct edge {
int to, cost;
};
const int maxn = + , INF = 0x3f3f3f3f;
int t, n, now, dist[maxn][maxn]; int Floyd_Warshall(int source, int End) {
for(int k = ; k <= n; k ++) {
for(int i = ; i <= n; i ++) {
for(int j = ; j <= n; j ++) {
if(dist[i][j] > dist[i][k] + dist[k][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
return dist[source][End];
} int main() {
cin >> t >> n;
int a, b, w;
for(int i = ; i <= n; i ++) {
for(int j = ; j <= n; j ++) {
if(i == j) dist[i][j] == ;
else dist[i][j] = INF;
}
}
for(int i = ; i < t; i ++) {
cin >> a >> b >> w;
dist[a][b] = min(dist[a][b], w);
dist[b][a] = min(dist[b][a], w);
}
cout << Floyd_Warshall(, n) << endl;
return ;
}
// */
这个题目就到此结束吧......
POJ-2387.Til the Cows Come Home.(五种方法:Dijkstra + Dijkstra堆优化 + Bellman-Ford + SPFA + Floyd-Warshall)的更多相关文章
- poj 2387 Til the Cows Come Home (最短路,dijkstra模版题)
题目 #define _CRT_SECURE_NO_WARNINGS #include<string.h> #include<stdio.h> #include<math ...
- POJ 2387 Til the Cows Come Home (图论,最短路径)
POJ 2387 Til the Cows Come Home (图论,最短路径) Description Bessie is out in the field and wants to get ba ...
- POJ.2387 Til the Cows Come Home (SPFA)
POJ.2387 Til the Cows Come Home (SPFA) 题意分析 首先给出T和N,T代表边的数量,N代表图中点的数量 图中边是双向边,并不清楚是否有重边,我按有重边写的. 直接跑 ...
- POJ 2387 Til the Cows Come Home
题目链接:http://poj.org/problem?id=2387 Til the Cows Come Home Time Limit: 1000MS Memory Limit: 65536K ...
- POJ 2387 Til the Cows Come Home(最短路 Dijkstra/spfa)
传送门 Til the Cows Come Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 46727 Acce ...
- 怒学三算法 POJ 2387 Til the Cows Come Home (Bellman_Ford || Dijkstra || SPFA)
Til the Cows Come Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 33015 Accepted ...
- POJ 2387 Til the Cows Come Home (最短路 dijkstra)
Til the Cows Come Home 题目链接: http://acm.hust.edu.cn/vjudge/contest/66569#problem/A Description Bessi ...
- POJ 2387 Til the Cows Come Home 【最短路SPFA】
Til the Cows Come Home Description Bessie is out in the field and wants to get back to the barn to g ...
- POJ 2387 Til the Cows Come Home Dijkstra求最短路径
Til the Cows Come Home Bessie is out in the field and wants to get back to the barn to get as much s ...
随机推荐
- springboot2.1.3.RELEASE+jsp笔记war部署tomcat
springboot+jsp <packaging>war</packaging> <parent> <groupId>org.springframew ...
- linux下一些重要命令的了解
linux下一些比较重要的命令: du命令: 查看使用空间: 格式: du [选项][文件] 参数: -a 显示目录中个别文件的大小. -b 显示目录或文件大小时,以byte为单位. -c 除了 ...
- oData 排序字段生成
跟踪SQL 发现生成的SQL中所有的字段都进行了排序,查看OData原代码,发现如果实体有Key,就按照Key asc 加上指定字段进行排序 属性 EnsureStableOrdering可以控制是否 ...
- Innodb中MySQL如何快速删除2T的大表
转自:http://database.51cto.com/art/201808/582324.htm OK,这里就说了.假设,你有一个表erp,如果你直接进行下面的命令: drop table erp ...
- 对poi-excel导出的浅层理解
上一篇对excel导入做了浅层的解释,本文将对导出再做浅层解释. 仍然是相同的套路,只不过是反过来而已. 反过来方向理论上本来是这样的:cell-->row-->sheet-->wo ...
- ES6中字符串模板的使用
反撇号(键盘上Tab键上面那个)基础知识 ES6引入了一种新型的字符串字面量语法,我们称之为模板字符串(template strings).除了使用反撇号字符代替普通字符串的引号 ‘ 或 ” 外,它们 ...
- 基础 - 字符读取函数scanf、getchar、gets、cin(清空缓存区解决单字符回车问题)
0x01 scanf.getchar.cin读取单字符: 如下: //scanf读取字符 回车问题 void Sub_1_1() { char v1,v2; scanf("%c", ...
- 如何判断ACCESS数据库有无密码
因为没有密码的数据库即使加上密码选项连接也不报错,所以如果通过连接来判读就无法识别无密码的数据库. 通过设置密码可以来测试数据库是否有密码,这是由于修改数据库密码的前提是数据库必须先有密码才行,如果数 ...
- Linux运维人员最常用166个命令汇总
引用自“菜鸟博客” 命令 功能说明 线上查询.帮助命令(2个) man 查看命令帮助,命令词典,更复杂还有info,但不常用. help 查看Linux内置命令的帮助,比如cd等命令. 文件.目录操作 ...
- Eclipse Debug不能热部署解决
摘要: Eclipse Debug不能热部署代码may be out-of-synch 问题描述:Project通过ant工具编译部署到JBoss下,在使用Eclipse远程调试功... ...