动态规划_每对结点间的最短路径_Floyd

问题描述

G = ( V , E ) G=(V,E) G=(V,E)是一个有 n n n个结点的带权有向图, w ( i , j ) w(i,j) w(i,j)是权函数
w ( i , j ) = { 边 < i , j > 上 的 权 值 if  < i , j > ∈ E 0 if  i = j ∞ if  < i , j > ∉ E w(i,j)=\begin{cases} 边<i,j>上的权值 &\text{if } <i,j>\in E \\ 0 &\text{if } i=j\\ \infty &\text{if } <i,j>\notin E \end{cases} w(i,j)=<i,j>0if <i,j>Eif i=jif <i,j>/E
每对结点间的最短路径问题是指图中任意一对结点 i i i j j j之间的最短路径。

分析

Dijkstra算法要求图中的边的权为非负值,而本问题中允许边的权为负值,但不允许路径长度为负值的回路,因为若结点 i i i到结点 j j j的路径上存在负值回路,则意味着结点 i i i到结点 j j j没有最短路径。

最优子结构特性

G = ( V , E ) G=(V,E) G=(V,E)是带权有向图,L(i,j)是从结点 i i i到结点 j j j的最短路径长度, k k k是这条路径上的一个结点, L ( i , k ) L(i,k) L(i,k) L ( k , j ) L(k,j) L(k,j)分别是从 i i i k k k和从 k k k j j j的最短路径长度,则必有 L ( i , j ) = L ( i , k ) + L ( k , j ) L(i,j)=L(i,k)+L(k,j) L(i,j)=L(i,k)+L(k,j),若不然,则 L ( i , j ) L(i,j) L(i,j)代表的路径就不是最短路径。

最优解值的递推关系

d − 1 [ i ] [ j ] = { w ( i , j ) if  < i , j > ∈ E ∞ if  < i , j > ∉ E d_{-1}[i][j]=\begin{cases} w(i,j) &\text{if } <i,j>\in E \\ \infty &\text{if } <i,j>\notin E \end{cases} d1[i][j]={w(i,j)if <i,j>Eif <i,j>/E
d k [ i ] [ j ] = min ⁡ { d k − 1 [ i ] [ j ] , d k − 1 [ i ] [ k ] + d k − 1 [ k ] [ j ] } , 1 ≤ k ≤ n − 1 d_k[i][j]=\min\{d_{k-1}[i][j],d_{k-1}[i][k]+d_{k-1}[k][j]\},1\leq k \leq n-1 dk[i][j]=min{dk1[i][j],dk1[i][k]+dk1[k][j]},1kn1
其中 d k [ i ] [ j ] d_k[i][j] dk[i][j]表示从结点 i i i到结点 j j j的路径上,只允许包含编号不大于 k k k的结点时,所以可能的路径中的最短路径的长度, d − 1 [ i ] [ j ] d_{-1}[i][j] d1[i][j]表示从 i i i j j j不包含结点(直达)的长度, L ( i , j ) = d n − 1 [ i ] [ j ] L(i,j)=d_{n-1}[i][j] L(i,j)=dn1[i][j]

重叠子问题

为了计算 d k [ i ] [ j ] d_k[i][j] dk[i][j]时,必须计算 d k − 1 [ i ] [ j ] , d k − 1 [ i ] [ k ] , d k − 1 [ k ] [ j ] d_{k-1}[i][j],d_{k-1}[i][k],d_{k-1}[k][j] dk1[i][j],dk1[i][k],dk1[k][j]

  • 邻接矩阵 a a a存储有向图
  • 二维数组 d d d用于保存各条最短路径的长度,其中 d [ i ] [ j ] d[i][j] d[i][j]存放从结点 i i i到结点 j j j的最短路径的长度
  • 二维数组 p a t h path path记录相应的最短路径, p a t h [ i ] [ j ] path[i][j] path[i][j]给出从结点 i i i到结点 j j j的最短路径中的前一个结点,可以反向追溯最短路径
  • 初始时 d [ i ] [ j ] = a [ i ] [ j ] d[i][j]=a[i][j] d[i][j]=a[i][j]
  • k = 0 , 1 , . . . , n − 1 k=0,1,...,n-1 k=0,1,...,n1,每次考察一个结点 k k k
  • 在算法的第 k k k步上应作出决策:从 i i i j j j的最短路径上是否包含结点 k k k

程序

#include<iostream>
using namespace std;

vector<vector<int> > a;//邻接矩阵
vector<vector<int> > d;//保存每对结点之间最短路径
vector<vector<int> > path;//标记函数
int n;//结点数

//创建邻接矩阵
void CreateA(){
	cin >> n;
	for(int i = 0;i < n;i ++){
		vector<int> v;
		int v1;
		for(int j = 0;j < n;j ++){
			cin >> v1;
			v.push_back(v1);
		}
		a.push_back(v);
	}
}

void allPath(){
	for(int i = 0;i < n;i ++){//初始化d
		for(int j = 0;j < n;j ++){
			d[i][j] = a[i][j];
		}
	}
	//迭代:对于点k,若i直接到j的距离大于1->k->j的距离和时,改写d[i][j]
	for(int k = 0;k < n;k ++){
		for(int i = 0;i < n;i ++){
			for(int j = 0;j < n;j ++){
				if(d[i][k]+d[k][j]<d[i][j])
					d[i][j] = d[i][k] + d[k][j];
			}
		}
	}
}
int main(){
	CreateA();
	allPath();
	return 0;
}
 

结束语

其实你并没有什么忘不掉的人,只是始终对自己那场没有结果的付出和被浪费的爱耿耿于怀。

作者:花城

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值