图论之最短路算法模板

从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径——最短路径。

现主要应用两种算法  单元最短路(spfa),任意两点间的最短路(floyd)

spfa

1.算法思想:(动态逼近法)

  设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。 
    松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对结点i,j进行松弛,就是判定是否dis[j]>dis[i]+w[i,j],如果该式成立则将dis[j]减小到dis[i]+w[i,j],否则不动。

void spfa(s);//求单源点到其他点的最近距离
	queue<int>q;//建立队列存放松驰过的点 
	for i=1 to n do{dis[i]=INF,vis[i]=false;}//初始化每点到s点的距离以及不在队列
	dis[s]=0;q.push(s);vis[s]=true;//初始化dis[s],源点入队并标记 
	while(!q.empty())
		u=q.front();q.pop();vis[u]=false;//队首出队,并取消标记 
		for each edge(v,i)
			if(dis[i]>dis[v]+mp[v][i])//判断是否被一条新的路径松弛 
				dis[i]=dis[v]+mp[v][i];
				if(!vis[i]){q.push(i),vis[i]=true;}//如果不在队列中,则进队  

2.判断负环:如果不再存在负环(即存在最短路径),则一定能求出最短路径。

  bfs :判断某一点进队次数是否大于等于总顶点数

 if(!vis[i]){q.push(i),cnt[i]++;if(cnt[i]>=n) return false;vis[i]=true;}

  dfs:留坑

3.输出路径:运用前缀数组的写法,即path【i】是i的前一个节点(父节点);输出时运用递归即可,注意为倒叙输出

if(dis[i]>dis[v]+mp[v][i])//判断是否被一条新的路径松弛 
	dis[i]=dis[v]+mp[v][i];
	path[i]=v;
void printpath(int k){
	while(k!=0){
		printf(k);
		k=path[k];
	}
}

4.模板:

有两种形式,一种是用邻接矩阵存储,另一种是用邻接表vector存储

void spfa(int s)
{
    queue<int>q;
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;vis[s]=true;q.push(s);
    while(!q.empty())
    {
        int now=q.front();q.pop();vis[now]=false;
        for(int i=0;i<G[now].size();i++)
        {
            int to=G[now][i];
            if(dis[to]>dis[now]+mp[now][to])
            {
                dis[to]=dis[now]+mp[now][to];
                if(!vis[to])
                {
                    vis[to]=true;
                    q.push(to);
                }
            }
        }
    }
}
View Code
void spfa(int s)
{
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(s);dis[s]=0;vis[s]=1;
    while(!q.empty())
    {
        int now=q.front();q.pop();vis[now]=0;
        for(int i=1;i<=n;i++)
        {
            if(dis[i]>dis[now]+mp[now][i])
            {
                dis[i]=dis[now]+mp[now][i];
                if(!vis[i]){
                    vis[i]=1;
                    q.push(i);
                }
            }
        }
    }
}
View Code

 floyd

1.算法思路: 

   Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)

   从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

void init()
{
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            mp[i][j]=(i==j)?0:INF;
}
for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(dp[i][j]>dp[i][k]+dp[k][j])
                 dp[i][j]=dp[i][k]+dp[k][j];

2.输出路径:path[i][j]代表从i到j要走的下一步;

for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        path[i][j]=j;
if(dp[i][j]>dp[i][k]+dp[k][j])
    dp[i][j]=dp[i][k]+dp[k][j];
    path[i][j]=path[i][k];
void printfpath(int i,int j)
{
    int tmp=i;
    printf("Path: %d",tmp);
    while(tmp!=j)
    {
        printf("-->%d",path[tmp][j]);
        tmp=path[tmp][j];
    }
    printf("\n");
}

 

转载于:https://www.cnblogs.com/vainglory/p/8480400.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值