图论(多源最短路径)

一、Floyd-Warshall算法

1、可以存在负权值的边,但不可存在负环;Floyd更适用于稠密图

2、对于图的最短路径满足最优子结构:路径p是从i到j的一条最短路径,结点k是路径p上的中间结点,那么从i到k是一条最短路径、从k到j也是一条最短路径

dis_{i,j,k}=min(dis_{i,j,k-1},dis_{i,k,k-1}+dis_{k,j,k-1})

在动态规划过程中,以每一个点为k点(中介),看是否可以松弛dis数组

int dis[maxn][maxn];
int pre[maxn][maxn];

memset(dis, 0x3f, sizeof(dis));
for (int i = 0; i < m; i++)
{
	int u, v, w;
	cin >> u >> v >> w;
	dis[u][v] = w;
}
for (int i = 1; i <= n; i++)
{
	dis[i][i] = 0;
}
for (int k = 1; k <= n; k++)
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (dis[i][j] > dis[i][k] + dis[k][j])
			{
				dis[i][j] = dis[i][k] + dis[k][j];
				pre[i][j] = k;
			}
		}
	}
}

3、计算完成后,dis数组记录两点间最短距离,pre用于记录路径

pre数组使用方法:如果想找1->0的最短路径,那么就是pre[1][0]=2、pre[1][2]=3、pre[1][3]=1路径回溯可知0->2->3->1

二、Johnson算法

1、对稀疏图来说,Johnson算法的渐进表现要优于Floyd,并且Johnson算法可以判断负环

2、Johnson算法使用的技术称为重新赋予权值,工作原理如下:如果所有边权值都是非负值,那么我们可以对每个结点运行一次Dijkstra来找到最短路径;如果图G包含权重为负值的边,但是没有负环,那么只要计算出一组新的非负权重值,再使用Dijkstra;如果包含负环,则返回-1

3、重新计算权值:

如果仅仅是对每个边进行简单的加法,那么会改变该条的最短路径,这样的方法会导致经过边数多的路径加上更多的边权

方法:寻找一个新结点s(编号为0),使s于其他结点的最初路径是0,然后计算出s到每个结点的最短路径(由于有负值,一定有一部分0变为负值),把最短路径保存在h数组中,然后利用如下公式重新赋予边权w'(u,v)=w(u,v)+h(u)-h(v),得到正边权

struct edge 
{
	int v, w, next;
} e[10005];
struct node {
	int dis, id;
	friend bool operator<(const node& a, const node& b)
	{
		return a.dis > b.dis;
	}
	node(int d, int x)
	{
		dis = d, id = x;
	}
};
int head[maxn], vis[maxn], t[maxn];
int cnt, n, m;
int h[maxn], dis[maxn][maxn];        //如果仅同时使用一个i对应的dis可以开一维
void addedge(int u, int v, int w)
{
	e[++cnt].v = v;
	e[cnt].w = w;
	e[cnt].next = head[u];
	head[u] = cnt;
}
int spfa(int s) 
{
	queue q;
	memset(h, 63, sizeof(h));
	h[s] = 0, vis[s] = 1;
	q.push(s);
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for (int i = head[u]; i; i = e[i].next) 
		{
			int v = e[i].v;
			if (h[v] > h[u] + e[i].w) 
			{
				h[v] = h[u] + e[i].w;
				if (!vis[v])
				{
					vis[v] = 1;
					q.push(v);
					t[v]++;
					if (t[v] == n + 1)
						return 0;
				}
			}
		}
	}
	return 1;
}
void dijkstra(int s) 
{
	priority_queue q;
	for (int i = 1; i <= n; i++) 
		dis[s][i] = inf;
	memset(vis, 0, sizeof(vis));
	dis[s][s] = 0;
	q.push(node(0, s));
	while (!q.empty())
	{
		int u = q.top().id;
		q.pop();
		if (vis[u])
			continue;
		vis[u] = 1;
		for (int i = head[u]; i; i = e[i].next)
		{
			int v = e[i].v;
			if (dis[s][v] > dis[s][u] + e[i].w)
			{
				dis[s][v] = dis[s][u] + e[i].w;
				if (!vis[v]) 
					q.push(node(dis[s][v], v));
			}
		}
	}
}
void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		addedge(u, v, w);
	}
	for (int i = 1; i <= n; i++) 
		addedge(0, i, 0);
	if (!spfa(0)) 
	{
		cout << -1 << endl;
		return;
	}
	for (int u = 1; u <= n; u++)
		for (int i = head[u]; i; i = e[i].next)
			e[i].w += h[u] - h[e[i].v];
	for (int i = 1; i <= n; i++) 
	{
		dijkstra(i);
		for (int j = 1; j <= n; j++)
		{
			dis[i][j] += h[j] - h[i];
		}
	}
}

你可能感兴趣的:(ACM知识(硬货),最短路径)