zoukankan      html  css  js  c++  java
  • 最短路板子

    链式前项星:

    struct E {
        int to, w, next;
    }edge[N]; //这里千万要注意,如果题目是双向边的话,这里的N要开2*N
    
    int tot, head[N];
    
    //加边
    inline void add_edge(int u, int v, int w) {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    
    //遍历代码
    for (int i = head[u]; !i; i=edge[i].next) { 
        int v=edge[i].to;
        int w=edge[i].w;
        //to do something
    }
    View Code

    读入挂(说真的,图论题不用读入挂真的是找T):

    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    View Code

    NOTE: 1. 时刻检查全局n与局部n有无重复定义

        2. 时刻注意有无初始化就算写了init(),也要看看有没有调用

        3. 看一看题目给的点是否从1开始

        4. 记住双向边要开两倍maxm

    单源最短路:

    在加权有向图的最短路径求解算法中,Dijkstra算法只能处理所有边的权值都是非负的图(是否有环不影响求解)

    基于拓扑顺序的算法虽然能在线性时间内高效处理负权重图,但仅局限于无环图

     

    基于拓扑顺序的算法O(V+E)

    void topu_sp(int s)
    {
        queue<int> q;
        while (!q.empty()) q.pop();
        memset(dis, inf, sizeof(dis));
        dis[s] = 0;
        for (int i = 1; i <= n; ++ i)
            if (dg[i] == 0) q.push(i); //入度为0入队列
    
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = head[u]; ~i; i = egde[i].nex) {
                int v = edge[i].to;
                int w = edge[i].w;
                dis[v] = min(dis[v], dis[u] + w);
                if (--dg[v] == 0) q.push(v); //将相连的点的入度-1,若出现0则入队
            }
        }
    }
    View Code

    Dijkstra + 优先队列 O((n+m)logm) (一般不会卡,用就对了)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #define ll long long
    #define pii pair<int, int>
    
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9+7;
    const int maxn = 2e5+7;
    using namespace std;
    struct node {int to,w,next;} edge[maxn];
    int head[maxn], cnt;
    int dis[maxn], vis[maxn];
    int n, m, s, t;
    
    inline ll read()
    {
        ll x = 0, f = 1;
        char ch = getchar();
        while (ch<'0' || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ( ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(dis,0x3f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        cnt = 0;
    }
    
    void add_edge(int u,int v,int w)
    {
        edge[cnt].to = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt ++;
    }
    
    void dijkstra(int s)
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
        dis[s] = 0; q.push({dis[s],s});
        while(!q.empty()) {
            int now = q.top().second;
            q.pop();
            if(vis[now]) continue;
            vis[now] = 1;
            for(int i = head[now]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if(dis[v] > dis[now] + edge[i].w) {
                    dis[v] = dis[now] + edge[i].w;
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    
    int main()
    {
        while(~scanf("%d%d",&m,&n)) {
            init();
            for(int i = 0; i < m; i++) {
                int u, v, w;
                scanf("%d%d%d",&u, &v, &w);
                add_edge(u, v, w), add_edge(v, u, w);
            }
            dijkstra(s);
            printf("%d
    ",dis[n]);
        }
    }
    View Code

    Dijkstra + 斐波那契堆 (待填)

    Dijkstra + 配对堆 (比堆快)

    View Code

    为此还需要一个更为普遍的最短路径求解算法:能够处理负权重图,也能处理有环的情况。

    这里注意SPFA容易被卡,如果O(VE)在规定时间内,那么建议使用Bellman-Ford

    cir数组是用来标记哪些点在被负权环影响的

    Bellman-Ford  O(VE) 

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    using namespace std;
    const int N = 500 + 10;
    const int M = 2500+ 300;
    const int inf = 0x3f3f3f3f;
    int dist[N];
    int head[N];
    bool cir[N];
    int n, m, s, tot;
    
    struct E {
        int to, next, w;
    }edge[M*2];
    
    inline void add_edge(int u, int v, int w) {
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    
    void bellman_ford(int s)
    {
        dist[s] = 0;
        for (int i = 1; i <= n; ++ i){
            for (int u = 1; u <= n; ++ u){
                if (dist[u] == inf) continue;
                for (int k = head[u]; ~k; k = edge[k].next) {
                    int w = edge[k].w, v = edge[k].to;
                    if (dist[v] > dist[u] + w) {
                        dist[v] = dist[u] + w;
                        if (i == n) cir[u] = cir[v] = 1;
                    }
                }
            }
        }
    }
    
    void init() {
        tot = 0;
        memset(head, -1, sizeof(head));
        memset(dist, inf, sizeof(dist));
    }
    
    int main() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        int T;
        cin >> T;
        while (T--) {
            cin >> n >> m >> s;
            init();
            int u, v, w;
            for (int i = 0; i < m; ++i) {
                cin >> u >> v >> w;
                add_edge(u, v, w);
                add_edge(v, u, w);
            }
            for (int i = 0; i < s; ++i) {
                cin >> u >> v >> w;
                add_edge(u, v, -w);
            }
            bellman_ford(s);
    
        }
        return 0;
    }
    View Code

    SPFA_DFS

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    #include<cmath>
    #include<string>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f; // 2122219134
    const int maxn = 500 + 10;
    const int maxm = 100000 + 10;
    struct node {
        int to;
        int w;
        int nex;
    }edge[maxm];
    int tot, n, m, w;
    int head[maxn], vis[maxn], dis[maxn], cnt[maxn];
    
    
    void init(){
        memset(cnt, 0, sizeof(cnt));//cnt[i]记录i入队的次数
        memset(dis, inf, sizeof(dis));//初始化dis
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        tot = 0;
    }
    
    void add_edge(int u, int v, int w){
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].nex = head[u];
        head[u] = tot++;
    }
    
    bool dfs_spfa(int u){
        vis[u] = 1;
        for(int i = head[u]; ~i; i = edge[i].nex) {
            int v = edge[i].to;
            if(dis[u] + edge[i].w < dis[v]) {
                dis[v] = dis[u] + edge[i].w;
                if (vis[v] || dfs_spfa(v)) {
                    vis[u] = 0;
                    return 1;
                }
            }
        }
        vis[u] = 0;
        return 0;
    }
    
    
    int main(){
        int T;
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin >> T;
        while (T--){
            init();
            cin >> n >> m >> w;
            for (int i = 0; i < m; ++ i){
                int u, v, w;
                cin >> u >> v >> w;
                add_edge(u, v, w), add_edge(v, u, w);
            }
            for (int i = 0; i < w; ++ i) {
                int u, v, w;
                cin >> u >> v >> w;
                add_edge(u, v, w * (-1));
            }
            dis[1] = 0; //这个初始化别忘了
            if (dfs_spfa(1)) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
        return 0;
    }
    View Code

     SPFA_BFS

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    #include<cmath>
    #include<string>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int INF = 0x3f3f3f3f; // 2122219134
    const int maxn = 500 + 10;
    const int maxm = 100000 + 10;
    struct node {
        int to;
        int w;
        int nex;
    }edge[maxm];
    int tot,n;
    int head[maxn],  dis[maxn], cnt[maxn];
    int vis[maxn], cir[maxn];
    
    void dfs(int u)
    {
        cir[u] = true; //cir用于标记是否被负环影响
        for(int i = head[u]; ~i; i = edge[i].nex) {
            int v = edge[i].to;
            if (!cir[v]) dfs(v);
        }
    }
    
    void init(){
        memset(cnt, 0, sizeof(cnt));//cnt[i]记录i入队的次数
        memset(dis, INF, sizeof(dis));//初始化dis
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        memset(cir, 0, sizeof(cir));
        tot = 0;
    }
    
    void add(int u, int v, int w){
        edge[tot].to = v;
        edge[tot].w = w;
        edge[tot].nex = head[u];
        head[u] = tot++;
    }
    
    bool bfs_spfa(int s)
    {
        dis[s] = 0;
        queue<int> q;
        q.push(s);
        vis[s] = 1;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = 0;
            for(int i = head[u]; ~i; i = edge[i].nex){
                int v = edge[i].to, w = edge[i].w;
                if(cir[v]) continue; //cir用于标记是否被负环影响
                if(dis[v] > dis[u] + w) {
                    dis[v] = dis[u] + w;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = 1;
                        ++cnt[v];
                        if(cnt[v] >= n)  dfs(v);
                    }
                }
            }
        }
        return 0;
    }
    
    int main(){
        int T;
        ios::sync_with_stdio(0);
        cin.tie(0);
        cin >> T;
        while (T--){
            init();
            int m, w;
            cin >> n >> m >> w;
            for (int i = 0; i < m; ++ i){
                int u, v, w;
                cin >> u >> v >> w;
                add(u, v, w), add(v, u, w);
            }
            for (int i = 0; i < w; ++ i) {
                int u, v, w;
                cin >> u >> v >> w;
                add(u, v, w*(-1));
            }
            if (bfs_spfa(1)) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
        return 0;
    }
    View Code

    求不带负权的环花费的算法,可以改进spfa算法,初始化d[start] = INF,d[other]=weight(s->other),并将处s外的点入队

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<map>
    #include<cstdio>
    #include<queue>
    #include<stack>
    
    using namespace std;
    
    const int INF = 0x3f3f3f3f;
    
    int cost[305][305];
    int dis[305];
    int n;
    bool vis[305];
    
    void spfa( int start ){
        stack<int> Q;
    
        for( int i = 1; i <= n; i++ ){
            dis[i] = cost[start][i];
            if( i != start ){
                Q.push( i );
                vis[i] = true;
            }
            else{
                vis[i] = false;
            }
        }
        dis[start] = INF;
    
        while( !Q.empty() ){
            int x = Q.top(); Q.pop(); vis[x] = false;
    
            for( int y = 1; y <= n; y++ ){
                if( x == y ) continue;
                if( dis[x] + cost[x][y] < dis[y] ){
                    dis[y] = dis[x] + cost[x][y];
                    if( !vis[y] ){
                        vis[y] = true;
                        Q.push( y );
                    }
                }
            }
        }
    }
    
    int main(){
        ios::sync_with_stdio( false );
    
        while( cin >> n ){
            for( int i = 1; i <= n; i++ ){
                for( int j = 1; j <= n; j++ ){
                    cin >> cost[i][j];
                }
            }
    
            int ans, c1, cn;
            spfa( 1 );
            ans = dis[n];
            c1 = dis[1];
            spfa( n );
            cn = dis[n];
    
    
            cout << min( ans, c1 + cn ) << endl;
        }
    
        return 0;
    }
    View Code

    多源最短路:

    Floyd-Warshall算法,中文亦称弗洛伊德算法,是解决任意两点间的最短路径的一种算法,

    可以正确处理负权(但不可存在负权回路)的最短路径问题。

    (以m=nlogn作为区别稀疏图与稠密图)

    Floyd O(n^3)  注意使用时,记得判断重边的情况

    bool floyd()
    {
        for (int k = 1; k <= n; ++ k)
            for (int i = 1; i <= n; ++ i){
                if (G[i][k] == inf) continue;
                for (int j = 1; j <= n; ++ j) {
                    if (G[k][j] == inf) continue;
                    if (G[i][j] > G[i][k]+G[k][j]) 
                        G[i][j] = G[i][k]+G[k][j];
                }
                if (G[i][i] < 0) return 1;
        }
        return 0;
    }
    View Code

    Johnson算法 O(nmlog⁡m)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<queue>
    #include<cstdlib>
    #define N 30010
    #define M 60010
    const int INF = 0x3f3f3f3f;
    using namespace std;
    
    int n,m,head[N],cnt=0,sum[N];
    long long h[N],dis[N];
    bool vis[N];
    struct Edge{
        int nxt,to,val;
    }ed[M];
    
    int read(){
        int x=0,f=1;char c=getchar();
        while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
        return x*f;
    }
    
    void add(int u,int v,int w){
        ed[++cnt].nxt=head[u];
        ed[cnt].to=v,ed[cnt].val=w;
        head[u]=cnt;
        return;
    }
    
    void spfa(){
        queue<int>q;
        memset(h,INF,sizeof(h));
        memset(vis,false,sizeof(vis));
        h[0]=0,vis[0]=true;q.push(0);
        while(!q.empty()){
            int u=q.front();q.pop();
            if(++sum[u]>=n){
                printf("-1
    ");exit(0);
            }
            vis[u]=false;
            for(int i=head[u];i;i=ed[i].nxt){
                int v=ed[i].to,w=ed[i].val;
                if(h[v]>h[u]+w){
                    h[v]=h[u]+w;
                    if(!vis[v]) q.push(v),vis[v]=true;
                }
            }
        }
        return;
    }
    
    void dijkstra(int s){
        priority_queue<pair<long long,int> >q;
        for(int i=1;i<=n;i++)
            dis[i]=INF;
        memset(vis,false,sizeof(vis));
        dis[s]=0;
        q.push(make_pair(0,s));
        while(!q.empty()){
            int u=q.top().second;q.pop();
            if(vis[u]) continue;
            vis[u]=true;
            for(int i=head[u];i;i=ed[i].nxt){
                int v=ed[i].to,w=ed[i].val;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    if(!vis[v]) q.push(make_pair(-dis[v],v));
                }
            }
        }
        return;
    }
    
    int main(){
        n=read(),m=read();
        int u,v,w;
        for(int i=1;i<=m;i++)
            u=read(),v=read(),w=read(),add(u,v,w),add(u,v,w);
        for(int i=1;i<=n;i++)
            add(0,i,0);
        spfa();
        //Johnson算法就相当于是给每条边都加上一个权值,跑n遍最短路
        //这个权值的来源是先跑一边spfa计算
        for(int u=1;u<=n;u++)
            for(int j=head[u];j;j=ed[j].nxt)
                ed[j].val+=h[u]-h[ed[j].to];
        //当然这里你也可以开二维的dis来记录最短路,方便查询
        for(int i=1;i<=n;i++){
            dijkstra(i);
            long long ans=0;
            for(int j=1;j<=n;j++){
                //所以这里要减去原先加上的权值,dis[j]+h[j]-h[i]才是最短路
                cout << (dis[j]+h[j]-h[i]) << " ";
            }
            cout << endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    软工试水日报 3/7
    软工试水日报 3/6
    软工试水日报 3/5
    软工试水日报 3/4
    软工试水日报 3/3
    大二下学期每日总结之第一次个人作业(第二阶段:生成excel)
    大二下学期每日总结之第一次个人作业(第一阶段)
    大二下学期每日总结之第一次个人作业(第一阶段)
    大二下学期每日总结
    大二下学期每日总结
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/12960200.html
Copyright © 2011-2022 走看看