最短路径问题:
寻找图中两个结点之间的最短路径。
求最短路算法:
a:dijkstra算法
解决指定起点和终点的最短路径,是以最短路径长度递增,逐次生成最短路径的算法,以起始点为中心向外层层扩展,直到扩展到终点为止。但不能处理负权边。
b:floyed算法
解决任意两点间的最短路径,可处理有向图或负权的最短路径,寻找任意中间点使得两点间的距离最短。
c:SFPA算法
是求单源最短路径的一种算法,可以判断是否存在负环(但无法处理带负环的图)
dijkstra 算法:
方法:一个带权图,从顶点出发,将顶点分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,之后每求出一条从源点出发的最短路径,就将顶点放到S数组中,这样S数组中从源点到每一个顶点都是最短路,直到将全部顶点都加入到S中,最短路已找到)第二组为其他未确定最短路径的顶点集合(用U表示)在加入点的过程中,要保持从源点到S中各顶点的最短路径长度不大于U数组中的点做中间节点到达顶点的长度。U数组中顶点的距离,是从源点以S数组中的点为中间点到顶点的距离
1,开始时,S只包括源点,其他点都在U中,若源点到U中的顶点可达,则初始化为距离,否则初始化为正无穷。
2,从U中选择当前距离源点最近的顶点k,并将其放到S中(该距离为源点到k的最短路径)
3,以k为中间点,比较修改源点到U中各顶点的距离,若源点v经过k到大顶点u的距离小于原来距离(之前不经过k时的距离),则修改源点到顶点u的距离,否则不修改。
4,重复2和3直到所有顶点都包含在S中。
例如:一个简单的图,源点为1,其他顶点有2,3 ;1到2的距离为3,1 到3 的距离为8,2到3 的距离为3;则开始时只有1在S数组里,2和3在U数组里,之后在U里选择顶点2,因为1到2的距离小于1到3的距离,然后以2为中间点,比较1经过2到大3的距离与1直接到达3的距离,发现1经国到达3的距离为6小于直接到3的距离,则修改1到3 的距离为6;当然这里如果1到3的距离为5的话就不修改咯。
void dijkstra(int start,int n)
{
int next,i,j,mindis;
for(i=1;i<=n;++i)
{
dis[i]=map[start][i];
visit[i]=0;
}
visit[start]=1;
for(i=1;i<=n;++i)
{
mindis=INF;
for(j=1;j<=n;++j)
{
if(!visit[j]&&dis[j]<mindis)
{
mindis=dis[j];
next=j;
}
}
visit[next]=1;
for(j=1;j<=n;++j)
{
if(!visit[j]&&dis[j]>dis[next]+map[next][j])
dis[j]=dis[next]+map[next][j];
}
}
}
floyed 算法:
方法:一个带权的图,从顶点u到顶点v,不是直接到达就是通过若干个结点到达。假设Dis ( i , j )为当前顶点 i 到顶点 v 的距离,对于每一个顶点k ,若Dis ( u ,v ) > Dis (u , k )+Dis( k , v )成立,则修改Dis ( u , v ),如此,遍历完所有的k 也就找到了从u 到v 的最短路径了。
1,初始化距离,若两顶点之间可达则为实际距离,不可达初始化为无穷大。
2,让 k 从1开始遍历,对于任意的从i 到j 是否满足从i 到 k 在到 j 的距离小于从i 到 j 的距离(i j 为任意顶点,相当于遍历所有顶点),若满足,则修改,之后,Dis ( i , j )所储存的值就是从i 到j 的最短路径距离了。(所以算法结束后得到的是任意两点间的最短距离)
void floyed(int n)
{
int i,j,k;
for(k=1;k<=n;++k)
{
for(i=1;i<=n;++i)
{
if(map[k][i]==INF)
continue;
for(j=1;j<=n;++j)
{
if(map[i][j]>map[i][k]+map[k][j])
map[i][j]=map[i][k]+map[k][j];
}
}
}
}
SPFA 算法:
方法:用邻接表来储存图,设立一个队列来保存待优化的结点,初始时将源点入队。每从队列中取出队头顶点u (消除标记)将与u 相连的所有点 v 进行松弛操作,如果能更新估计值,则更新,更新之后,若v 没有在队列中,要将点v 入队(标记),若已经在队列中,则不用处理。循环操作,直到队列为空。
判断有无负环:若某个点进入队列的次数超过n 次,则存在负环(SPFA无法处理带负环的图)
1,初始化,dis 数组初始化为无穷大,vis 标记数组初始化为0,源点的vis 数组标记为1,dis 数组距离为0 ,used 数组初始化为0,记录顶点入队的次数(判断负环是否存在)源点used 数组为1,已使用一次。之后将源点入队。
2,取队头元素u 并消除标记之后要删除,对与u 相连的所有顶点v 进行松弛操作,即比较dis[v] > dis[ u ]+edge[ i ].val ,若满足则更像dis[ v] 的值,若v 并不在队列中,则入队,标记并used[ v ] ++表示入队次数加1
3,循环2,直到队列为空为止。
邻接表建图
struct node
{
int from,to,val,next;
};
node edge[maxm];
void initialize()
{
cnt=0;
mark=0;
memset(head,-1,sizeof(head));
}
void add(int a,int b,int c)
{
edge[cnt].from=a;
edge[cnt].to=b;
edge[cnt].val=c;
edge[cnt].next=head[a];
head[a]=cnt++;
}
SPFA 代码实现
void spfa(int s)
{
int flag=0;
queue<int>q;
int vis[maxn],dis[maxn],used[maxn];
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(used,0,sizeof(used));
vis[s]=1;
dis[s]=0;
used[s]++;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].val)
{
dis[v]=dis[u]+edge[i].val;
if(!vis[v])
{
vis[v]=1;
used[v]++;
if(used[v]>n)
{
flag=1;
break;
}
q.push(v);
}
}
}
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}