概要
Floyd可以一次性求出所有节点之间的最短距离
- 这种算法主要采用了动态规划的思想,假设求从i到j的最短路径,那么寻找一个中间位置k,如果从i到k距离加上k到j的距离比直接从i到j小,那么就更新从i到j的最短路径
- 根据上面的设想,Floyd是由三层for循环组成的,第一层for循环显然是k,因为我们不知道谁来借助k,所以k应该放在最外面;里面的两层分别是i和j,这不难理解。
朴素的Floyd如下
void Floyd(int n){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j];
}
}
}
}
- 可以看出Floyd算法时间复杂度是O(n3),只能解决一些小图的最短路问题,但是代码书写简单,且能一次性求出所有边的最短路
练习
hdu2544
hdu2544
这道题可用floyd,不加任何优化,旨在熟悉算法框架
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 500;
int INF = 0x3f3f3f3f;
int edge[MAXN][MAXN];
void Floyd(int n){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j];
}
}
}
}
int main(){
int n,m,a,b,c;
while(cin>>n>>m){
if(n == 0&&m == 0) break;
memset(edge,0x3f,sizeof edge);
for(int i=0;i<m;i++){
cin>>a>>b>>c;
edge[a][b] = min(edge[a][b],c);
edge[b][a] = edge[a][b];
}
Floyd(n);
cout<<edge[1][n]<<endl;
}
return 0;
}
hdu3631
- 题目要求借助标记过的点寻找最短路,显然是floyd算法思想,又n范围不大,考虑使用floyd
- 需要注意自己到自己的距离问题,需要设置为0,输出格式注意一下
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+100;
int edge[500][500];
int vis[MAXN];
const int INF = 0x3f3f3f3f;
void floyd(int k,int n){
for(int i=0;i<n;i++){
if(edge[i][k]!=INF)
for(int j=0;j<n;j++){
if(edge[i][j] > edge[i][k] + edge[k][j]){
edge[i][j] = edge[i][k] + edge[k][j];
}
}
}
}
int main(){
int n,m,q,a,b,c,op;
int times = 1;
while(~scanf("%d%d%d",&n,&m,&q)){
if(n == 0&&m == 0&&q == 0) break;
if(times != 1) printf("\n");
memset(edge,0x3f,sizeof edge);
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++) edge[i][i] = 0;
for(int i=0;i<m;i++){
scanf("%d%d%d",&a,&b,&c);
if(edge[a][b] > c) edge[a][b] = c;
}
printf("Case %d:\n",times++);
while(q--){
scanf("%d",&op);
if(op == 0){
scanf("%d",&a);
if(vis[a]) {
printf("ERROR! At point %d\n",a);
}
else {
vis[a] = 1;
floyd(a,n);
}
}else{
scanf("%d%d",&a,&b);
if(!vis[a]||!vis[b]) printf("ERROR! At path %d to %d\n", a, b);
else if(edge[a][b] == INF) printf("No such path\n");
else printf("%d\n",edge[a][b]);
}
}
}
return 0;
}
hdu1704
- 题目意思是给出了若干比赛关系,问不能确定的冠军数量
- 抽象成一个图,那么很明显如果两个点之间可达,不论顺序,那么他们之间就能确定谁是冠军
此题是使用floyd算法求图的传递闭包,有关传递闭包概念如下
- 所以直接使用floyd算法,如果从i到j和j到i都没有路径,则构成一个答案
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+100;
int edge[600][600];
const int INF = 0x3f3f3f3f;
void floyd(int n){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(edge[i][k] != INF)
for(int j=1;j<=n;j++){
if(edge[i][j] > edge[i][k] + edge[k][j]){
edge[i][j] = edge[i][k] + edge[k][j];
}
}
}
}
}
int main(){
int t,n,m,x,y;
cin>>t;
while(t--){
memset(edge,0x3f,sizeof edge);
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>x>>y;
edge[x][y] = 1;
}
floyd(n);
int ans = 0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(edge[i][j] == INF&&edge[j][i] == INF){
ans++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
洛谷P1119
- 看数据范围应该是floyd。道路不需要修,而村庄需要修,每修一个村庄相当于图中加一个点,如果每加一个点就跑一次floyd那么显然不合理,题目给出村庄修复时间是升序,这是很方便的,可以考虑每次加的点作为floyd中的中介点k,按照规定时间范围遍历,如果x和y能够借助k互相达到,那么更新最短路,这个时候不需要考虑x或者y是否修好,因为如果没修好就输出-1,但是最短路需要更新
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
#include <sstream>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e5+100;
int Data[MAXN];
int edge[300][300];
int TIME[MAXN];
int Floyd(int n, int x, int y, int t, int &k){
for(;k<n;k++){
if(t < TIME[k]) break;
for(int i=0;i<n;i++){
if(edge[i][k] != INF){
for(int j=0;j<n;j++){
if(edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j];
}
}
}
}
if(t < TIME[x] || t < TIME[y]) return -1;
if(edge[x][y] == INF) return -1;
else return edge[x][y];
}
int main(){
int n,m,q,x,y,w,t;
int k = 0;
cin>>n>>m;
memset(edge, 0x3f, sizeof edge);
for(int i=0;i<n;i++) cin>>TIME[i];
for(int i=0;i<m;i++){
cin>>x>>y>>w;
if(w < edge[x][y]) edge[x][y] = edge[y][x] = w;
}cin>>q;
for(int i=0;i<q;i++){
cin>>x>>y>>t;
cout<<Floyd(n,x,y,t,k)<<endl;
}
return 0;
}