Bellmen_Ford和SPFA

Bellmen算法用来解决单源最短路问题,即给定图G和起点s,通过算法得到s到其他每个点的最短距离。它可以处理Dijkstra不能处理的带负权边图
SPFA是它的优化版本。
Bellmen通过 V − 1 V-1 V1 V V V是顶点个数)此迭代,每次遍历所有边:对于每条边 u − > v u->v u>v,如果以 u u u为中介点能使d[v]更小,则更新 d [ v ] d[v] d[v]。如果第 n n n此迭代,仍有可松弛的 d [ ] d[] d[],则说明图内存在负环。时间复杂度是 O ( V E ) O(VE) O(VE)
简单证明思路:把源点s想象成树的根节点,最短路确定后,因为对于不存在负环的图,最短路中一条边最多走一次,一棵最短路数也确定下来了,如图:
在这里插入图片描述
一开始 d [ s ] = 0 d[s]=0 d[s]=0第一层 s s s被确定。第一次迭代,第二层 B , C B,C B,C被确定下来,第二次第三层 D , E D,E D,E被确定下来。由于只有 V V V个点,层数不会超过 V V V层,迭代次数不会超过 V − 1 V-1 V1次。证毕。
Bellmen_Ford代码

const int maxn=1e3+10;
struct ac{
    int v;int d;
    ac(int _v=0,int _d=0){
        v=_v;
        d=_d;
    }
};
vector<ac> g[maxn];
int d[maxn];
int n;
bool Bellmen_Ford(int s){
    memset(d,0x3f,sizeof d);
    d[s]=0;
    for(int i=0;i<n-1;i++){
        for(int u=1;u<=n;u++){
            for(int j=0;j<g[u].size();j++){
                int v=g[u][j].v;
                int dis=g[u][j].d;
                if(d[v]>d[u]+dis){
                    d[v]=d[u]+dis;
                }
            }
        }
    }
    for(int u=1;u<=n;u++){  		//判断是否存在负环 
        for(int j=0;j<g[u].size();j++){
            int v=g[u][j].v;
            int dis=g[u][j].d;
            if(d[v]>d[u]+dis){
                return false;
            }
        }
    }
    return true;
}




spfa使用队列,只对于上一次迭代过松弛的点,加入队列中,下一次遍历它的出边。在大部分数据中这个算法异常高效,并且经常优于堆优化的Dijkstra。无负环的时间复杂度是 O ( K E ) O(KE) O(KE), K K K的期望不超过2。但是在有负环时,时间复杂度退化到 O ( V E ) O(VE) O(VE)
SPFA代码

const int maxn=1e3+10;
struct ac{
    int v,d;
    ac(int _v=0,int _d=0){
        v=_v;
        d=_d;
    }
};
vector<ac> g[maxn];
int d[maxn];
int n;
int inq[maxn];
int inqcnt[maxn];
bool SPFA(int s){
    memset(inq,false,sizeof inq);
    memset(d,0x3f,sizeof d);
    memset(inqcnt,0,sizeof inqcnt);
	queue<int> Q;
    Q.push(s);
    inq[s]=true;
    d[s]=0;
    inqcnt[s]++;
    while(!Q.empty()){
    	int u=Q.front();
    	Q.pop();
    	inq[u]=false;
    	for(int i=0;i<g[u].size();i++){
    		int v=g[u][i].v;
    		int dis=g[u][i].d;
    		if(d[v]>d[u]+dis){
    			d[v]=d[u]+dis;
    			if(inq[v]==false){
    				Q.push(v);
    				inq[v]=true;
    				inqcnt[v]++;
    				if(inqcnt[v]>=n) return false; //有可达负环 
				}
			}
		}
	}
	return true; //无可达负环 
}



二维点状图SPFA

#include<stdio.h>
#include<iostream>
#include<queue>
#include<string.h>
#include<stack>
using namespace std;
const int maxn=1e5+7;
bool inq[maxn][maxn];
char g[maxn][maxn];
int d[maxn][maxn];
//移动方式
int X[]={1,-1,0,0};
int Y[]={0,0,1,-1};
int n,m;
void bfs(int sx,int sy){
    queue<pair<int,int> >q;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            d[i][j]=10000000;
        }
    }
    memset(inq,0,sizeof inq);
    inq[sx][sy]=1;
    q.push(make_pair(sx,sy));
    d[sx][sy]=0;
    while(!q.empty()){
        int ox=q.front().first,oy=q.front().second;
        q.pop();
        inq[ox][oy]=0;
        for(int i=0;i<4;++i){
            int tx=ox+X[i],ty=oy+Y[i];
            //'#'为障碍物
            if(0<tx&&tx<=n&&0<ty&&ty<=m&&g[tx][ty]!='#'&&inq[tx][ty]==0){
                if(d[tx][ty]>1+d[ox][oy]){
                    d[tx][ty]=1+d[ox][oy];
                    inq[tx][ty]=1;
                    q.push(make_pair(tx,ty));
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值