蒟蒻复习之-----Floyd
暴力无解算法 (入门必备)
//然而我发现这个简单的算法还有很多用法
//作为复习篇不讲原理
图的要求:既可以是无向图也可以是有向图,边权可以为负,但是不能存在负环
复杂度为O(n^3)
##原理##
(dp思路)f[i][j] = f[i][k] + f[k][j]
代码
void floyd() {
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = min(f[i][k] + f[k][j], f[i][j]);
}
}
}
}
注意
1应对所建的图进行初始化如下
void init() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = inf; //求最大值时为 -inf
if(i == j) f[i][j] = 0;
}
}
}
2输入时注意判重边
for(int i = 1; i <= m; i++) {
int u,v,w;
cin>>u>>v>>w;
f[u][v] = min(w,f[u][v]);
}
##完整代码##
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1000 + 10;
const int inf = 1<<29;
int n,m;
int f[maxn][maxn];
void init() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = inf;
if(i == j) f[i][j] = 0;
}
}
}
void floyd() {
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = min(f[i][k] + f[k][j], f[i][j]);
}
}
}
}
int main() {
cin>>n>>m;
init();
for(int i = 1; i <= m; i++) {
int u,v,w;
cin>>u>>v>>w;
f[u][v] = min(w,f[u][v]);
}
floyd();
cout<<"QAQ";
return 0;
}
#应用#
##1.传递闭包##
判断图的连通性
**f[i][j] = f[i][j] || ( f[i][k] && f[k][j] ) **
void floyd() {
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = f[i][j] || (f[i][k] && f[k][j]);
}
}
}
}
##2.输出经过K个点后的最短路##
从floyd中dp转移中可以知道
枚举k个点表示经过k个点后的最短路
void floyd() {
for(int k = 1; k <= n; k++) { //可以根据题目所说的顺序转移
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
f[i][j] = min(f[i][k] + f[k][j], f[i][j]);
}
}
//此处输出经过k个点后的最短路
}
}
##3.找 i 到 j 最短路的路径##
添加一个矩阵p,p[i][j]表示i到j的最短行径中的j之前的第1个点
void floyd() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
p[i][j] = j;
}
}
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(f[i][k] + f[k][j] < f[i][j]) {
f[i][j] = f[i][k] + f[k][j];
p[i][j] = p[i][k];
}
}
}
}
//打印路径
for(int i = 1; i <= n; i++) {
for(int j = i + 1; j <= n; j++) {
int t = i;
while(t != j) {
cout<<t<<"->";
t = p[t][j];
}
cout<<j<<endl;
}
}
}
##4.找最小环##
求图中的最小环。先考虑无向图。
Floyd算法保证了最外层循环到k的时候所有点对之间的最短路只经过1∼k−1号节点。
环至少有3个节点,设编号最大的为x,与之直接相连的两个节点为u和v。
环的长度应为f[u][v][x-1]+w[v][x]+w[x][u]。其中w为边权,如不存在边则为无穷大。
我们只要在进行第x次迭代之前枚举所有编号小于k的点对更新答案即可。
int ans = INF;
for(int k = 1; k <= n; k++) {
for(int i = 1; i < k; i++) {
for(int j = i+1; j < k; j++) {
ans = min(ans, f[i][j] + a[j][k] + a[k][i]);
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
##例题##
[luogu 2047]社交网络(统计路径数)
[luogu 2419]牛大赛Cow Contest(传递闭包)
[Codeforces 295B]Greg and Graph(输出经过K个点后的最短路)
[poj1734]Sightseeing trip(Floyd)(最小环+路径)