warshall算法:
warshall算法是求二元关系穿闭包的算法.设关系R的关系图为G,设图G的所有顶点为v1,v2,…,vn,则t(R)的关系图可用该方法得到:若G中任意两顶点vi和vj之间有一条路径且没有vi到vj的弧,则在图G中增加一条从vi到vj的弧,将这样改造后的图记为G’,则G’即为t(R)的关系图。G’的邻接矩阵A应满足:若图G中存在从vi到vj路径,即vi与vj连通,则A[i,j]=1,否则A[i,j]=0。
这样,求t(R)的问题就变为求图G中每一对顶点间是否连通的问题。
定义一个n阶方阵序列A(0),A(1),A(2),…,A(n),每个方阵中的元素值只能取0或1。A(m)[i,j]=1表示存在从vi到vj且中间顶点序号不大于m的路径(m=1..n),A(m)[i,j]=0表示不存在这样的路径。而A(0)[i,j]=1表示存在从vi到vj的弧,A(0)[i,j]=0表示不存在从vi到vj的弧。
这样,A(n)[i,j]=1表示vi与vj连通,A(n)[i,j]=0表示vi与vj不连通。故A(n)即为t(R)的关系矩阵,n表示每个中间节点的标号都不大于n,比如A(1)表示只能用第一个节点作为中间节点,A(n)表示所有节点都可以作为中间节点。
下面是代码:
void warshall(int a[N][N])
{
for (int k = 0;k < N;k++)
for (int i=0;i < N;i++)
for (int j = 0;j < N;j++)
a[i][j] |= a[i][k] & a[k][j];
}
Floyed-warshall算法:
该算法使用来求任意两点之间最短距离的算法,跟warshall算法差不多,该算法要求求最短路径是不允许出现负权回路,求最长路径时,不允许出现正权回路。
void floyed_warshall(int a[N][N])
{
for (int k = 0;k < N;k++)
for (int i=0;i < N;i++)
for (int j = 0;j < N;j++)
a[i][j]=max(a[i][j],a[i][k]+a[k][j]);
}
Dijkstra算法:
该算法求有向加权图最短路径问题,条件是所有边权值非负。
算法具体步骤
(1)初始时,S只包含源点,即S=,v的距离为0。U包含除v外的其他顶点,U中顶点u距离为边上的权(若v与u有边)或 )(若u不是v的出边邻接点)。
(2)从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u(u U)的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
(4)重复步骤(2)和(3)直到所有顶点都包含在S中。
代码如下:
void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
{
bool s[maxnum]; // 判断是否已存入该点到S集合中
for(int i=1; i<=n; ++i)
{
dist[i] = c[v][i];
s[i] = 0; // 初始都未用过该点
if(dist[i] == maxint)
prev[i] = 0;
else
prev[i] = v;
}
dist[v] = 0;
s[v] = 1;
// 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
// 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
for(int i=2; i<=n; ++i)
{
int tmp = maxint;
int u = v;
// 找出当前未使用的点j的dist[j]最小值
for(int j=1; j<=n; ++j)
if((!s[j]) && dist[j]
Bellman-Ford算法:
Bellman-ford算法是求含负权图的单源最短路径算法,效率很低,但代码很容易写。即进行不停地松弛,每次松弛把每条边都更新一下,若n-1次松弛后还能更新,则说明图中有负环,无法得出结果,否则就成功完成。Bellman-ford算法有一个小优化:每次松弛先设一个旗帜flag,初值为FALSE,若有边更新则赋值为TRUE,最终如果还是FALSE则直接成功退出。Bellman-ford算法浪费了许多时间做无必要的松弛,所以SPFA算法用队列进行了优化,效果十分显著,高效难以想象。SPFA还有SLF,LLL,滚动数组等优化。
Dijkstra算法中不允许边的权是负权,如果遇到负权,则可以采用Bellman-Ford算法。
Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是 边集E的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到 图G的任意顶点v的最短路径d[v]。
适用条件&范围
1.单源最短路径(从源点s到其它所有顶点v);
2.有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);
3.边权可正可负(如有负权回路输出错误提示);
4.差分约束系统;
Bellman-Ford算法描述:
1,.初始化:将除源点外的所有顶点的最短距离估计值d[v] ←+∞, d[s] ←0;
2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在d[v]中。
描述性证明:
首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。
其次,从源点s可达的所有顶点如果 存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。
在对每条边进行1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1条边,所以,只需要循环|v|-1次。
每实施一次松弛操作,最短路径树上就会有一层顶点达到其最短距离,此后这层顶点的最短距离值就会一直保持不变,不再受后续松弛操作的影响。(但是,每次还要判断松弛,这里浪费了大量的时间,怎么优化?单纯的优化是否可行?)
如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果d[v]仍保持+∞,则表明从s到v不可达。
如果有负权回路,那么第|v|-1遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。
代码如下:
typedef struct edge
{
int v;//起点
int u;//终点
int w;
} edge;
edge edges[200004];
int d[1004];
int maxData = 1000000000; //此处要特别注意,bellman-ford算法中不要使用0x7fffffff
int edgenum;
bool BellmanFord(int s)
{
int i,j;
bool flag = false;
for(i=1; id[edges[j].v]+edges[j].w)
{
flag =true;
d[edges[j].u]=d[edges[j].v]+edges[j].w;
}
}
if(!flag)
break;
}
//判断是否存在负权回路 for(i=0; id[edges[i].v]+edges[i].w)
{
return false;
}
}
return true;
}
主函数中:
edgenum=0;
for(i=0; i>start>>end>>w;
edges[edgenum].v = start;
edges[edgenum].u = end;
edges[edgenum].w = w;
edgenum++;
}
SPFA(shortest path faster algorithm)算法:
我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
设Dist代表S到I点的当前最短距离,Fa代表S到I的当前最短路径中I点之前的一个点的编号。开始时Dist全部为+∞,只有Dist[S]=0,Fa全部为0。
维护一个队列,里面存放所有需要进行迭代的点。初始时队列中只有一个点S。用一个布尔数组记录每个点是否处在队列中。
每次迭代,取出队头的点v,依次枚举从v出发的边v->u,设边的长度为len,判断Dist[v]+len是否小于Dist[u],若小于则改进Dist[u],将Fa[u]记为v,并且由于S到u的最短距离变小了,有可能u可以改进其它的点,所以若u不在队列中,就将它放入队尾。这样一直迭代下去直到队列变空,也就是S到所有的最短距离都确定下来,结束算法。若一个点入队次数超过n,则有负权环。
下面是几段代码:
1.
void spfa(int s,int m)
{
int i,k,ts=0,te=1;
Q[ts] = s;
dis[s] = 0;
while(ts0 && dis[i] - g[k][i] > dis[k])
{
dis[i] = dis[k] + g[k][i];
if(!visit[i])
{
Q[te++] = i;
visit[i] = true;
}
}
}
ts++;
}
}
2.
#include
using namespace std;
#include
#define MAXN 10
#define INF 100000000
struct node
{
int to;
int w;
node * next;
};
node * List[MAXN];
int path[MAXN];
int dist[MAXN];
int vis[MAXN];
int n;
void SPFA(int v0)
{
int u;
for(int i=0;i q;
q.push(v0);
while(!q.empty())
{
u=q.front();
q.pop();
temp=List[u];
while(temp!=NULL)
{
int v=temp->to;
if(dist[v]>dist[u]+temp->w)
{
dist[v]=dist[u]+temp->w;
path[v]=u;
if(!vis[v])
{
q.push(v);
vis[v]++;
}
}
temp=temp->next;
}
}
}
void show(int i)
{
if(i==0)
return;
show(path[i]);
printf("%d->",i);
}
int main()
{
scanf("%d",&n);
int u,v,w;
node* temp;
memset(List ,0,sizeof(List));
while(1)
{
temp=new node;
scanf("%d%d%d",&u,&v,&w);
if(u==-1&&v==-1&&w==-1)
break;
temp->next=NULL;
temp->to=v;
temp->w=w;
if(List[u]==NULL) List[u]=temp;
else
{
temp->next=List[u];
List[u]=temp;
}
}
SPFA(0);
for(int i=1;i",0);
show(path[i]);
printf("%d\n",i);
}
system("pause");
return 0;
}
/*
7
0 1 6
0 2 5
0 3 5
1 4 -1
2 1 -2
2 4 1
3 2 -2
3 5 -1
4 6 3
5 6 3
-1 -1 -1
*/
3.邻接表实现
#include
#include
using namespace std;
const long MAXN=10000;
const long lmax=0x7FFFFFFF;
typedef struct
{
long v;
long next;
long cost;
}Edge;
Edge e[MAXN];
long p[MAXN];
long Dis[MAXN];
bool vist[MAXN];
queue q;
long m,n;//点,边
void init()
{
long i;
long eid=0;
memset(vist,0,sizeof(vist));
memset(p,-1,sizeof(p));
fill(Dis,Dis+MAXN,lmax);
while (!q.empty())
{
q.pop();
}
for (i=0;i
总结基本就是这样,先掌握,在以后的实践中继续深化!
http://www.cnblogs.com/mycapple/archive/2012/08/12/2634227.html