您的位置:首页 > 其它

Bellman变形得来的SPFA最短算法(不能含负环);Floyd求两点间最短路,求有向图的闭包;

2010-06-17 10:26 357 查看
#include <iostream>
#include <queue>
using namespace std;

const int MAXN=100;

/*********************************/
*  SPFA算法需要的空间分配
/*********************************/
queue<int> q;
bool inq[MAXN];

/*********************************/
* 构造稀疏矩阵的邻接表 表示法
* u,v:边的端点
* first:表结点
* next:弧结点
* w: 边的权值
* n:顶点数目 m:边数目
/*********************************/

int u[MAXN];
int v[MAXN];
int w[MAXN];
int first[MAXN];
int next[MAXN];
int n,m;

/*********************************/
*  读入图到邻接表中 (无向图)
/*********************************/

void readG1()
{
memset(first,-1,sizeof(first));
cin>>n>>m;
int i,j;
for(i=0,j=0;i<m;++i)
{
int a,b,c;
cin>>a>>b>>c;
u[j]=a;
w[j]=c;
next[j]=first[u[j]];
first[u[j]]=j;
v[j++]=b;
u[j]=b;
v[j]=a;
w[j]=c;
next[j]=first[u[j]];
first[u[j]]=j;
v[j++]=a;
}
}

/*********************************/
*  读入图到邻接表中 (有向图)
/*********************************/
void readG2()
{
memset(first,-1,sizeof(first));
cin>>n>>m;
int i;
for(i=0;i<m;++i)
{
int a,b,c;
cin>>a>>b>>c;
u[i]=a;
v[i]=b;
w[i]=c;
next[i]=first[u[i]];
first[u[i]]=i;
}
}

/*********************************/
* 普通队列+标记数组=SPFA
* 实际就是Bellman的一个小变形得来的
/*********************************/

int d[MAXN];

void BellmanFord_SPFA()
{
// 顶点0作为源点
for(int i=0;i<n;++i)
{
d[i]=i==0? 0:MAXN;
}
memset(inq,0,sizeof(inq));
q.push(0);
while(!q.empty())
{
int x=q.front();
q.pop();
inq[x]=false;
for(int e=first[x];e!=-1;e=next[e])
{
if(d[v[e]]>d[x]+w[e])
{
d[v[e]]=d[x]+w[e];
if(!inq[v[e]])
{
inq[v[e]]=true;
q.push(v[e]);
}
}
}
}
}

/*********************************/
* Floyd 算法的实现 (有向图)
* Floyd 实现有向图的闭包
* d[i][j] 表示i到j的最短路径
* c[i][j] =1表示i到j可以连通,=0表示不可连通
/*********************************/

int dd[MAXN][MAXN];
int c[MAXN][MAXN];

void Floyd()
{
//预处理
for(int i=0;i<n;++i)
{
for(int k=0;k<n;++k)
{
dd[i][k]= i==k ? 0:MAXN;
c[i][k]= i==k ? 1:0;
}
for(int j=first[i];j!=-1;j=next[j])
{
dd[i][v[j]]=w[j];
c[i][v[j]]=1;
}
}
//Floyd两点间最短路算法
for(int k=0;k<n;++k)
{
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
{
if(dd[i][k]<MAXN&&dd[k][j]<MAXN)
{
if(dd[i][j]>dd[i][k]+dd[k][j])
{
dd[i][j]=dd[i][k]+dd[k][j];
}
}
c[i][j]=c[i][j]||(c[i][k]&&c[k][j]);
}
}
}
}

int main()
{
/*测试Floyd算法
4 5
0 1 1
0 2 3
0 3 3
1 3 1
1 2 1
*/

readG2();
Floyd();
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
{
cout<<i<<","<<j<<"----"<<dd[i][j]<<"-----"<<c[i][j]<<endl;
}
}

/*
SPFA测试:0为源点

*/
BellmanFord_SPFA();

for(int i=0;i<n;++i)
{
cout<<i<<"--0 :"<<d[i]<<endl;
}
return 0;
}


 

 

依旧采取静态数组邻接表 来构图, 这样使用非常方便, 只要在邻接表内的边都是存在的边, 而没有的边均是不通的边, 输入起来很方便, 使用时候也很方便.

 

Bellman变形得到的就是SPFA算法, 复杂度比bellman更低一些,比迪克拉斯算法适用范围更广,边权可以有负值, 但是不能带负环,否则绕着负环无限走下去,总能变得越来越小.

 

这些最短路算法都是使用了松弛原理, 只是松弛时又做了一些小变化.

 

SPFA : 初始化源点的d[]=0,其他的d[]=INF.

源点入队, 每次取队头出队列, 标记它不在队列内, 如果可以更新它所关联的端点, 并且端点不在队列内,则加入队列.

这样,每个端点都可能重复进入队列,但是经过有限次松弛, 总会使所有的端点的最短路径都无法再次更新. Dij算法区别于使用了

优先队列, 每次出队的结点已经完美,无需再次进入,而这里使用的是普通队列. Dij只允许正权图使用.

 

还有Floyd算法, 可以求任意两点间最短路径, O(n3)复杂度, 写起来很简单, 需要做一些初始化.

d[][]=INF。

然后修正以下内容:

d[i][i]=0,c[i][i]=1. 即自身可与自身连通,到自身路径是0.

d[i][j]=w[i][j]

然后可以使用算法了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 c include 测试 im