Floyd算法详解
写在前面
在做洛谷的树上dp题单的时候遇到了一道题目P1613 跑路发现自己对flyod的理解太浅薄了,于是去重新学习了一遍,又做了几道题目,然后结合了acwing的算法提高课的总结,于是乎有了这篇博客。
要说floyd就不能只说板子
什么是floyd,提到floyd肯定就会想到最短路。
Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。
我们现在看到的形式
for(int k=1;k<=n;k++){
for(int i=1;i<=n;++j){
for(int k=1;k<=n;++k){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
现在我们的板子中经常看到的floyd都是二维,其实最原始的状态是三维。
状态
转移
转移的时候是以经不经过第
不经过:
经过:
所以状态转移方程为:
空间优化
观察上面的状态转移方程可以看到在算第
我们可以看一下去掉第一维会不会得到的方程是否和原方程等价.
也就是说我们在去点第一维后,在算第
是不是这样呢?
要想看
先考虑
当
由于
所以这是等价变形的.
然后可以考虑
然后我们就可以证明这两个状态转移方程是等价的.也就有了新的状态转移方程
kij or ijk
从dp的角度考虑也就很自然的会得出循环k在在最外层.
具体的为什么在最外层知乎上已经很多回答了.可以看下面的链接
为什么是kij不是ijk
应用
求多源最短路
题意是给了一堆牧场
#include <bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pdd pair<double,double>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define vi vector<int>
using namespace std;
int n,m;
const int N=200;
const db inf=1e20;
db d[N][N],mv[N];
pdd a[N];
char g[N][N];
void solve() {
cin>>n;
rep(i,1,n) {
cin>>a[i].x>>a[i].y;
}
rep(i,1,n) {
cin>>g[i]+1;
}
auto dist=[&](pdd a, pdd b) {
db dx=a.x-b.x;
db dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
};
rep(i,1,n) {
rep(j,1,n) {
if(i==j) {
d[i][j]=0;
} else if(g[i][j]=='1') {
d[i][j]=dist(a[i],a[j]);
} else {
d[i][j]=inf;
}
}
}
//跑一遍floyd
rep(k,1,n) {
rep(i,1,n) {
rep(j,1,n) {
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
//预处理出来每个点到连通块其他点的最短路的最大值
db ans1=0,ans2=inf;
rep(i,1,n) {
rep(j,1,n) {
if(d[i][j]<inf) {
mv[i]=max(mv[i],d[i][j]);
}
}
ans1=max(ans1,mv[i]);
}
// cout<<ans2<<endl;
rep(i,1,n) {
rep(j,1,n) {
if(d[i][j]>=inf) {
ans2=min(ans2,mv[i]+mv[j]+dist(a[i],a[j]));
}
}
}
cout<<setiosflags(ios::fixed)<<setiosflags(ios::right)<<setprecision(6)<<max(ans1,ans2)<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// freopen("1.in", "r", stdin);
int _;
// cin>>_;
// while(_--)
solve();
return 0;
}
求传递闭包
传递闭包
:在有向图中,如果a->b,并且b->c那么a->c.换句话说将所有能间接到达的点都连上直接的边后的图就是原图的传递闭包
oiwiki上面用bitset进行的优化
思路:
时间复杂度:
代码有点长
#include <bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pdd pair<double,double>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define vi vector<int>
using namespace std;
void solve() {
int n,m;
while(cin>>n>>m,n||m){
vector<vector<int>>g(n+1,vector<int>(n+1));
vector<vector<int>>d(n+1,vector<int>(n+1));
auto floyd=[&](){
rep(k,0,n-1){
rep(i,0,n-1){
rep(j,0,n-1){
d[i][j]|=d[i][k]&&d[k][j];
}
}
}
};
auto check=[&](){
rep(i,0,n-1){
if(d[i][i]) return 2;
}
rep(i,0,n-1){
rep(j,0,i-1){
if(!d[i][j]&&!d[j][i]){
return 0;
}
}
}
return 1;
};
int type=0,turn=0;
rep(i,1,m){
string s;
cin>>s;
int u=s[0]-'A',v=s[2]-'A';
if(!type){
d[u][v]=1;
floyd();
type=check();
if(type){
turn=i;
}
}
}
if(!type){
cout<<"Sorted sequence cannot be determined."<<endl;
}else if(type==2){
cout<<"Inconsistency found after "<<turn<<" relations."<<endl;
}else{
vector<int>st(n+1,0);
auto getmn=[&](){
rep(i,0,n-1){
if(!st[i]){
int flag=0;
rep(j,0,n-1){
if(!st[j]&&d[j][i]){
flag=1;
break;
}
}
if(!flag){
st[i]=1;
char c='A'+i;
return c;
}
}
}
};
cout<<"Sorted sequence determined after "<<turn<<" relations: ";
rep(i,0,n-1){
cout<<getmn();
}
cout<<"."<<endl;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// freopen("1.in", "r", stdin);
int _;
// cin>>_;
// while(_--)
solve();
return 0;
}
考虑加上优化
每加入一条边之后其实并不需要再重新跑一遍
时间复杂度:
#include <bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pdd pair<double,double>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define vi vector<int>
using namespace std;
void solve() {
int n,m;
while(cin>>n>>m,n||m){
vector<vector<int>>g(n+1,vector<int>(n+1));
vector<vector<int>>d(n+1,vector<int>(n+1));
auto check=[&](){
rep(i,0,n-1){
if(d[i][i]) return 2;
}
rep(i,0,n-1){
rep(j,0,i-1){
if(!d[i][j]&&!d[j][i]){
return 0;
}
}
}
return 1;
};
int type=0,turn=0;
rep(i,1,m){
string s;
cin>>s;
int u=s[0]-'A',v=s[2]-'A';
if(!type){
d[u][v]=1;
auto kkk=[&](){
rep(i,0,n-1){
//处理能到达v能到的所有点
if(d[v][i]) d[u][i]=1;
//所有能到u的点都能到v
if(d[i][u]) d[i][v]=1;
//所有能到u的点不包含u
if(d[i][u]){
rep(j,0,n-1){
//所有v能到的点不包含v
if(d[v][j]){
d[i][j]=1;
}
}
}
}
};
kkk();
type=check();
if(type){
turn=i;
}
}
}
if(!type){
cout<<"Sorted sequence cannot be determined."<<endl;
}else if(type==2){
cout<<"Inconsistency found after "<<turn<<" relations."<<endl;
}else{
vector<int>st(n+1,0);
auto getmn=[&](){
rep(i,0,n-1){
if(!st[i]){
int flag=0;
rep(j,0,n-1){
if(!st[j]&&d[j][i]){
flag=1;
break;
}
}
if(!flag){
st[i]=1;
char c='A'+i;
return c;
}
}
}
};
cout<<"Sorted sequence determined after "<<turn<<" relations: ";
rep(i,0,n-1){
cout<<getmn();
}
cout<<"."<<endl;
}
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// freopen("1.in", "r", stdin);
int _;
// cin>>_;
// while(_--)
solve();
return 0;
}
求无向图的最小环
AcWing 344. 观光之旅
oiwiki上面无向图找环说的很清楚。
、
关于这道题目的一个难点是记录状态。
#include <bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define fep(i,a,b) for(int i = (a); i >= (b); --i)
#define pii pair<int, int>
#define pdd pair<double,double>
#define ll long long
#define db double
#define endl '\n'
#define x first
#define y second
#define pb push_back
#define vi vector<int>
using namespace std;
void solve() {
int n,m;
cin>>n>>m;
//g用于存原图,因为求最小环的时候需要原图中的边长
vector<vi>g(n+1,vi(n+1,0x3f3f3f3f));
vector<vi>pos(n+1,vi(n+1));
//d用于做floyd的最短路径
rep(i,1,m){
int u,v,w;
cin>>u>>v>>w;
g[u][v]=g[v][u]=min(g[u][v],w);
}
vector<vi>d=g;
int res=0x3f3f3f3f;
vi ans;
auto dfs=[&](auto &&dfs,int i, int j)->void{
if(!pos[i][j]) return;
int k=pos[i][j];
dfs(dfs,i,k);
ans.pb(k);
dfs(dfs,k,j);
};
auto get=[&](int i, int j,int k){
ans.clear();
ans.pb(k);
ans.pb(i);
dfs(dfs,i,j);
ans.pb(j);
};
rep(k,1,n){
//枚举i,j找最小环
rep(i,1,k-1){
rep(j,i+1,k-1){
if(res>d[i][j]+g[i][k]+g[k][j]){
res=d[i][j]+g[i][k]+g[k][j];
get(i,j,k);
}
}
}
//正常floyd
rep(i,1,n){
rep(j,1,n){
if(d[i][j]>d[i][k]+d[k][j]){
d[i][j]=d[i][k]+d[k][j];
pos[i][j]=k;
}
}
}
}
if(res==0x3f3f3f3f){
cout<<"No solution."<<endl;
}else{
for(auto it:ans){
cout<<it<<' ';
}
cout<<endl;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// freopen("1.in", "r", stdin);
int _;
// cin>>_;
// while(_--)
solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署