应用
- 求多源点最短路
- 传递闭包
- 找最小环(对于正权图而言)
- 恰好经过k条边的最短路
floyd算法原理
算法模板
//初始化:d[i][i] = 0 且不相连的节点距离需要初始化为INF
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
空间优化
- 需要说明表达式
d[k][i][j] = min(d[k][i][j] , d[k-1][i][k] + d[k-1][k][j])
与d[i][j] = min(d[i][j] , d[i][k] + d[k][j])
等价 - 以
d[k-1][i][k] <=> d[i][k]
为例,只需要说明每一次d[i][k]
的值可能被更新时,d[i][k]
的值不变 - 令
j=k
, 则d[k][i][k] = min(d[k][i][k] , d[k-1][i][k] + d[k-1][k][k])
.因为d[k-1][k][k]=0
,所以d[i][k]
的值不会发生改变,总是第k-1
层的值
例题-牛的旅行
题解
1.根据输入建立邻接矩阵d
,跑一边floyd算法
2.求每一个连通块的直径(距离最远的两点的距离),答案必定>=每一连通块的直径
3.枚举连接两个连通块内的某点所形成的新连通块的直径中的最小值,答案必定>=这个最小值
4.新的连通块直径(例如连接i,j两点):maxd[i] + dist(i , j) + maxd[j]
代码
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 160;
const double INF = 1e20;
#define x first
#define y second
typedef pair<int ,int> PII;
PII p[N];
double d[N][N] , maxd[N];
char g[N][N];
int n;
double get_dist(PII a , PII b)
{
double x = a.x - b.x , y = a.y - b.y;
return sqrt(x*x + y*y);
}
int main()
{
cin >> n;
for(int i = 0; i < n; i++) cin >> p[i].x >> p[i].y;
for(int i = 0; i < n; i++) cin >> g[i];
//初始化距离
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(i != j)
{
if(g[i][j] == '1') d[i][j] = get_dist(p[i] , p[j]);
else d[i][j] = INF;
}
//Floyd算法
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
//求连通块内的直径
for(int i = 0; i < n; i++)
for(int j = 0;j < n; j++)
if(d[i][j] < INF)
maxd[i] = max(maxd[i] , d[i][j]);
//情况1:结果必然 >= 每一连通块的直径
double ans = 0;
for(int i = 0; i < n; i++) ans = max(ans , maxd[i]);
//情况2:结果必然 >= 连接一条边后新的连通块的直径(枚举最小值)
double res = INF;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(d[i][j] >= INF)
res = min(res , maxd[i] + get_dist(p[i] , p[j]) + maxd[j]);
printf("%lf
" , max(ans , res));
return 0;
}
例题-排序(传递闭包)
floyd算法求传递闭包
- 1.邻接矩阵初始化d[i][j]:1-表示i,j之间存在i到j的单向路径 0-表示不存在路径
- 2.将转移关系修改为:d[i][j] = d[i][j] | ( d[i][k] & d[k][j] ) -(表示 i->k , k->j两段路径都存在)
- 3.整体认识:在算法结束后,所有的间接关系,例如 i->j , j->k,会直接表示出来,即d[i][k] = 1
题解
- 在本题中可以将A < B类比成A->B
- 则存在矛盾例如A<B , B<C , C<A , 可以推知A<A , 即A->A , d[i][i] = 1
- 变量A,B不存在相对关系:d[a][b] = 0 && d[b][a] = 0
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 30;
int d[N][N] , g[N][N];
int n , m;
void floyd()
{
memcpy(d , g , sizeof g);
for(int k = 0; k < n; k++)
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j ++)
d[i][j] |= d[i][k] & d[k][j];
}
int check()
{
for(int i = 0; i < n; i++)
if(d[i][i]) return 2;
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
if(!d[i][j] && !d[j][i]) return 0;
return 1;
}
char get_min(int val)
{
for(int i = 0; i < n; i++)
{
int cnt = 0;
for(int j = 0; j < n; j++)
if(d[i][j]) cnt++;
if(cnt == val) return i + 'A';
}
}
int main()
{
while(cin >> n >> m , n || m)
{
char str[4];
//type: 0-表示存在无法判断的关系 1-表示所有关系已确定 2-关系存在矛盾
int type = 0 , t;
memset(g , 0 , sizeof g);
for(int i = 1; i <= m; i++)
{
cin >> str;
int a = str[0] - 'A' , b = str[2] - 'A';
if(!type)
{
g[a][b] = 1;
floyd();
type = check();
if(type) t = i;
}
}
if(!type) printf("Sorted sequence cannot be determined.
");
else if(type == 2) printf("Inconsistency found after %d relations.
" , t);
else
{
printf("Sorted sequence determined after %d relations: " , t);
for(int i = 0; i < n; i++)
printf("%c" , get_min(n - i - 1));
puts(".");
}
}
return 0;
}
参考文献
Acwing-算法提高课-图论章节
https://www.acwing.com/activity/content/introduction/16/