2021-01-30

最短路

一.任意两点间的最短路径(Floyd)
1.
借用了动归的思想,d[i,j]保存i到j 的最短路长度
动态转移方程:d[ i , j ]=min(d[ i , j ],d[ i, k ]+d[ k , j ])

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++) {
				dis[i][j] = Min(dis[i][j], dis[i][k]+dis[k][j]);
			}
		}
	}
}

虽然floyd算法的效率比较低,但也有优点
(1)可以一次性求出所有结点之间的最短路径;
(2)能处理有负权边的图(但不能处理负圈)
判断负圈
在初始化的时候,如果没有把dis[i][i]为0的话,dis[i][i]=dis[i][u]+。。。+dis[v][i]就是在外面绕一圈回来的路径,借助这一点可以用来判断负圈;
负圈:环路上边的权值之和为0
只要在floyd中判断是否存在某个dis[i][i]<0就行了。
2.传递闭包
在交际网络中,给定若干个元素和若干个对二元关系,且关系具有传递性。“通过传递性推导出来尽量多的元素之间的惯性系”的问题称之为传递闭包。
d[i,j]=1表示i和j有关系,=0则表示没有关系。特别的d[i,i]始终为1;

poj-3660

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<cstdlib>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=101;
const int inf=0x3f3f3f3f; 
int mp[maxn][maxn];
int u,v,w,n,m;
int main(){
	scanf("%d %d",&n,&m);
	memset(mp,0,sizeof(mp)); 
	while(m--){
		scanf("%d%d",&u,&v);
		mp[u][v]=1;
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				mp[i][j]=mp[i][j]||(mp[i][k]&&mp[k][j]);
			}
		}
	}
	int tot1=0;
	for(int i=1;i<=n;i++){
		int tot=0;
		for(int j=1;j<=n;j++){
			if(mp[i][j]||mp[j][i]) tot++;
		}
		if(tot==n-1) tot1++;
	}
	printf("%d\n",tot1);
}

二.单源最短路径
1.dijkstra算法
算法流程:(1)初始化dist[1]=0,其余节点的dist的值为无穷
(2)找出一个未被标记的、dist[x]最小的节点x,然后标记节点x
(3)扫描节点x所有的出边(x,y,z),若dist[y]>dist[x]+z,则使用dist[x]+z 更新dist[y]
(4)重复上午2~3两个步骤,直到所有节点被标记
优先队列写法

const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int head[N], ver[N], edge[M], Next[M], d[N];
bool v[N];
int n, m, tot;
priority_queue<pair<int, int>>q;
void add(int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
void dijkstra() {
	memset(d, inf, sizeof(d));
	memset(v, 0, sizeof(v));
	d[1] = 0;
	q.push(make_pair(0, 1));
	while (!q.empty()) {
		int x = q.top().second;
		q.pop();
		if (v[x])continue;
		v[x] = 1;
		for (int i = head[x]; i; i = Next[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				d[y] = d[x] + z;
				q.push(make_pair(-d[y], y));
			}
		}
	}
}

2.bellman-ford和SPFA算法
算法流程:
(1)建立一个队列,最初队列中只含有1
(2)取出对头节点x,扫描它的所有出边(x,y,z),若dist[y]>dist[x]+x,则使dist[x]+更新dist[y]。同时若y不在队列,则把y入队。
(3)重复上述操作,知道队列为空。
bellman-ford和SPFA算法可以在有负权的情况下正常工作,而dijkstra不行。

const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int head[N], ver[N], edge[M], Next[M], d[N];
bool v[N];
int n, m, tot;
queue<int>q;
void add(int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
void spfa() {
	memset(d, inf, sizeof(d));
	memset(v, 0, sizeof(v));
	d[1] = 0;
	q.push(1);
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		v[x] = 0;
		for (int i = head[x]; i; i = Next[i]) {
			int y = ver[i], z = edge[i];
			if (d[y] > d[x] + z) {
				d[y] = d[x] + z;
				if (!v[y])q.push(y), v[y] = 1;
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值