最短路
最短路问题分为多源最短路(求两点之间的最短路) 和 单源最短路(求所有点到某特定点的最短距离)。感觉和前面的最小生成树的感觉差不多,但是相比还是复杂一点(算法也多)。
算法:
Dijkstra算法
不带负权的有向或者无向图
如何实现这个算法
- 首先找到一点其边最小
- 然后以这个为中间,往外扩到两家连接起来(扩展的时候找最小的或最大的(根据题目要求))
代码:
priority_queue<pair<int,int>>q;
//大根堆(优先队列),pair的二维为结点编号
//pair的第一维为dist的相反数(利用相反数变成小根堆)
void dijkstra()
{
memset(d,0x3f,sizeof(d));
memset(v,0,sizeof(v)); //结点进行标记
d[1]=0;
q.push(make_pair(0,1);
while(q.size())
{
int x=q.top().second;q,pop(); //取出堆顶
if(v[x]) continue;
v[x]=1;
for(int i=head[x];i;i=Next[i]) //扫描出边数
{
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
q.push(make_pair(-d[y],y);) //元素插入堆
}
}
}
}
Folyd算法及求解最小环
正、负权的有向或者无向图都可,但是不可以有负环
可以求出每对点之间的最短距离
如何实现这个算法
运用到动态规划的知识!!(博客主的动态规划讲解)就是进行枚举进行比较和和实验。
代码:
for(k=1;k<=n;k++) //枚举中点
for(i=1;i<=n;i++) //枚举起点
for(j=1;j<=n;j++) //枚举终点
if((a[i][k]!=INF)&&(d[k][j]!=INF)&&(d[i][k]+d[k][j]<d[i][j]))
d[i][j]=d[i][k]+d[k][j];
Bellman-Ford算法
求解单源点最短路问题,正负权边都可以进行
如何实现这个算法
- 首先对所有顶点的最短距离进行估值
- 同样的使每个顶点之间的距离都逼近最小
- 检查负权回路:
判断边两端是否收敛。如果未收敛的顶点,则返回false;否则返回true,并且从源点可达的顶点最短距离保存在d[v]。
代码:
bool bellman(int v0)
{
int i,j;
for(i=1;i<=nv;i++) d[i]=maxx;
d[v0]=0;
for(i=1;i<=nv-1;i++)
for(j=1;j<=ne;j++)
if(d[edge[j].a+edge[j].w]<d[edge[j].b])//进行比较出最小的那个边
d[edge[j].b]=edge[j].w+d[edge[j].a]; //加入其中
for(j=1;j<=ne;j++)
if(d[edge[j].a]+edge[j].w<d[edge[j].b]) return false; //负权返回
return true;
}
SPFA算法
可以判断负环(如果某个点弹出队列的次数超过n-1次,则是负环),但是对于存在负环的图,无法计算单源最短路。
如何实现这个算法
- 初始化,然后对进入的点进行标记
- 从队首中取出一个点i,标记i已经出队,接着对与i点有边相连的j点进行松弛操作,如果松弛成功,则对j入队的次数进行检查,如果大于等于n,说明出现负环,算法结束。否则改进dist[j]的值,再检查j是否在队列中,如果不在,就将j点加入队尾。
代码:
void spaf()
{ //s为源点,vst[]记录点是否在队列中,距离值为dist[];
memset(d,0x3f,sizeof(d));
memset(v,0,sizeof(v));
d[1]=0;v[1]=1;
q.push(1);
while(q,size())
{
intx=q.front();q.pop();
v[x]=0;
for(int i=head[x];i;i=Next[i])
{
int y=ver[i].z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
if(!v[y])q.push(y),v[y]=1;
}
}
}
}