蒟蒻复习之-----Floyd

14 篇文章 0 订阅

蒟蒻复习之-----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)(最小环+路径)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值