图论的一些基础算法的模板

Floyd 算法:

作用:求任意两点最短路

时间:O(V^3)

#define for0(i,a,b) for (int i=a;i<b;i++)
memset(dp,INF,sizeof dp);
for0(i,0,n) dp[i][i] = 0;
int u,v,w;
for0(i,0,m){
    scanf("%d %d %d",&u,&v,&w);
    dp[u][v] = min(dp[u][v],w);
    dp[v][u] = min(dp[v][u],w);
}
for0(k,0,n)
    for0(i,0,n)
        for0(j,0,n)
            dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);

dp[i][j]表示两个点之间可以用1~k-1作为中途点时的最短路径,输入取最小值防止有权值不同的重边

判断有无负环检查是否有dp[i][i]<0,有负环的话就没有最短路径了


堆优化dijkstra 算法:

作用:求单源最短路

时间:O(E*logE)

const int N = 1e5+5;///点的数量
const int M = 5e5+5;///边的数量

struct Edge//储存边
{
    int to;
    int last;
    int w;
}edge[M];
int head[N],id;

void add(int u,int v,int w)//建从u->v,权值为w的边
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}

void init()//建边前的初始化
{
    memset(head,0,sizeof head);
    id = 1;
}

struct node//储存点
{
    int now;
    int w;//到达now节点的这条边的权值
    bool operator < (const node& a)const{//***比较方式要和自己想的反过来***
        return w>a.w;
    }
};

bool vis[N];//是否求出最短路径

void dijkstra()
{
    memset(vis,0,sizeof vis);
    priority_queue<node>que;
    int root = 1;//单元最短路径的源点
    que.push({root,0});
    while (!que.empty()){
        node now = que.top();que.pop();
        if (vis[now.now]) continue;
        vis[now.now] = true;
        /*
        当前点now记录了这个点到源点的最短距离,问题可以在这儿处理
        */
        for (int i=head[now.now];i!=0;i=edge[i].last){
            que.push({edge[i].to,edge[i].w+now.w});//***权值记得要加now.w***
        }
    }
}

不能处理有负权的边的问题,否则当前连到树上的点不一定是最短路径。

跑完dijkstra后相当于建了一棵包含所有节点的树,树上所有点到源点的距离最近。


Bellman_ford 算法

作用:求带负边的单源最短路/判负环

时间:O(V*E)

#define INF 0x3f3f3f3f
const int N = 1e4+5;
const int M = 5e4+5;

struct Edge
{
    int to,last,w;
}edge[M];

int id,head[N];

void add(int u,int v,int w)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}

void init()
{
    id = 1;
    memset(head,0,sizeof head);
}

int dis[N];

void Bellman_ford(int V)//V表示节点数
{
    int root = 1;//若只是判断负环,随便取一个源点即可
    memset(dis,INF,sizeof dis);
    dis[root] = 1;
    for (int k=1;k<V;k++)
        for (int u=1;u<=V;u++)
            for (int i=head[u];i!=0;i=edge[i].last){
                int v = edge[i].to;
                if (dis[v]>dis[u]+edge[i].w){
                    dis[v] = dis[u] + edge[i].w;
                    /*
                    若要判断负环,将最外层循环修改为k<=V,并判断k==V时是否还有被修改的点
                    有则证明有负环,因为有负环会一直更新
                    */
                }
            }
    /*
    求得的dis[i]即为i到root的最短路径
    */    
}

第k次更新dis[i]相当于在更新:i沿着边反向走k步途中能到达的所有点形成的这个图中,i距离源点的最短路径

上一次我们求出了k-1步下每个点的单元最短路径,因此我们用所有能直接一步到达i的节点去更新即可。(也可以说是BFS)

https://www.cnblogs.com/lfri/p/9521271.html   \leftarrow我讲不清,这篇文章讲的清楚)

当然有些点其实在第k次被更新好了,然后又去更新了别人,这样对那个被更新的点最终单源最短路的结果没有影响,就是可能提前获取了答案。

显然更新完V-1步,所有点的单源最短路都正确了,因为V-1步已经包含整张图了。还能更新说明有负环。


SPFA 算法(队列优化的Bellman_ford)

作用:求单源最短路/判负环

时间:O(V*E)  (一般情况远低于)

const int N = 1e4+5;
const int M = 5e4+5;

struct Edge
{
    int to,last,w;
}edge[M];

int id,head[N];

void add(int u,int v,int w)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].last = head[u];
    head[u] = id++;
}

void init()
{
    id = 1;
    memset(head,0,sizeof head);
}

int val[N];//记录该节点被更新多少次
int dis[N];//记录单源最短路

void SPFA(int V)//V表示节点数
{
    memset(dis,INF,sizeof dis);
    memset(val,0,sizeof val);

    int root = 1;//若只用于判负环,源点可以随便取
    bool flag = false;//是否有负环,初始化为无
    dis[root] = 0;
    /*
    下面注释掉的四行是SPFA的优化,节点数多的时候一般来说是会优化...
    就是先让dis[]小的去松弛,这样可以让进队列的数更少
    */
    //deque<int>que;
    //que.pb(root);
    queue<int>que;
    que.push(root);

    while (!que.empty()){
        int now = que.front();que.pop_front();
        int v;
        for (int i=head[now];i!=0;i=edge[i].last){
            v = edge[i].to;
            if (dis[v]>dis[now]+edge[i].w){
                dis[v] = dis[now] + edge[i].w;
                //que.pb(v);
                //if (dis[que.front()]<dis[que.back()]){que.pf(v);que.pop_back();}
                que.push(v);
                val[v]++;
                if (val[v]==V) {flag = true;break;}
            }
        }
        if (flag) break;
    }
}

优化原理是未被松弛成功的点一定不会对下一轮松弛产生影响,因此用队列保留被松弛过的点去更新即可。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值