图论 最短路

y总:n 表示点数 m表示边数


  • 最短路: memset(d,0x3f,sizeof d); INF = 0x3f3f3f3f;

  • 图的储存:稀疏图 邻接表,稠密图 邻接矩阵 (n^2 与 m 的数量级)

  • TLE时考虑 各种初始化 memset(dist,0x3f,sizeof dist);

用邻接表时一定要先初始化 memset(h,-1,sizeof h);

  • 无向图 g[a][b] = g[b][a] = min(g[a][b],c); add(b,a,c); 注意:e[ ]、ne[ ]、w[ ] 数组范围多开一倍

  • 互相比对来理解加深

dijkstra
  1. 朴素版dijkstra

#include <bits/stdc++.h>

using namespace std;

const int N = 1010,INF = 0x3f3f3f3f;

int n,m,sx,sy;
int g[N][N],dist[N];
bool st[N];

int dijkstra() // S集合:已确定的从sx点出发到点的距离是最短的
{
    memset(dist,0x3f,sizeof dist); 
    dist[sx] = 0; // 以某个点作为出发点
                        // 如果 下面就会再次 赋为 true  Segmentation Fault
    //st[1] = true; (错误)  第一次 必定是 t = 1  因为 dist[1] = 0 
    
    for(int i = 0;i < n;i++) // 迭代n个点 
    {
        int t = -1;
        for(int j = 1;j <= n;j++) // 遍历 不在 S集合中的距离最小的点
          if(!st[j] && (t == -1 || dist[j] < dist[t]))
           t = j; // 找到距 t 最近的点
        
        st[t] = true; // 将 t 加入 S集合中(当前已确定最短距离的点)
        
        for(int j = 1;j <= n;j++)
          dist[j] = min(dist[j],dist[t] + g[t][j]); // 用 t 更新
    }                          // 与 堆优化版  distance 相比对
    
    return d[sy];
}

int main()
{
    cin >> n >> m >> sx >> sy;
    
    for(int i = 1;i <= n;i++)
      for(int j = 1;j <= n;j++)
        if(i == j) g[i][j] = 0;
        else g[i][j] = INF;
    
    for(int i = 0;i < m;i++)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        g[a][b] = min(g[a][b],c); // 如果无向图 直接增加 g[b][a] = min(g[b][a],c);
    }                             //    或者 g[a][b] = g[b][a] = min(g[a][b],c);
    
    int t = dijkstra();
    if(t == 0x3f3f3f3f) cout << "-1" << endl;
    else cout << t << endl;
    
    return 0;
}

  1. 堆优化版dijkstra

#include <bits/stdc++.h>

using namespace std;

typedef pair<int,int> PII;
const int N = 1010,INF = 0x3f3f3f3f,M = 20010;
                                      // 1000 * 2 + 10
int n,m,sx,sy;
int h[N],e[M],ne[M],idx,w[M];
int dist[N];
bool st[N];
priority_queue<PII,vector<PII>,greater<PII>> heap;

void add(int a,int b,int c)
{
    w[idx] = c;
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

int dijkstra()
{
    memset(dist,0x3f,sizeof dist); 
    dist[sx] = 0;
    heap.push({0,sx}); // first为距离  second为节点
                       // 强调 先后顺序不能变  first second
    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();
        
        int distance = t.first,ver = t.second;
                               // 同朴素版的 S集合 
        if(st[ver]) continue; // 此处直接调过该节点  所以下面的if 不能加
        st[ver] = true;
        
        for(int i = h[ver];i != -1;i = ne[i])
        {
            int j = e[i];                 // w[i] i 是不断迭代的ne[i]  对应储存的 idx
            if(dist[j] > distance + w[i]) // 注: 此处 if判断不能加 (!st[j] &&  )
            {
                dist[j] = distance + w[i];
                heap.push({dist[j],j});
            }
               
            
        } 
    }
    
    return dist[sy];
}

int main()
{
    cin >> n >> m >> sx >> sy;
    
    memset(h,-1,sizeof h);
    
    for(int i = 0;i < m;i++)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c); // 如果是 无向图  add(a,b,c); add(b,a,c);
    }
    
    int t = dijkstra();
    if(t == INF) cout << "-1" << endl;
    else cout << t << endl;
    
    return 0;
}

bellman_ford
#include <bits/stdc++.h>

using namespace std;

const int N = 510,INF = 0x3f3f3f3f,M = 10010;

struct Edge
{
    int x,y,c;
}tu[M];

int n,m,k,sx,sy;
int dist[N],backup[N];

int bellman_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[sx] = 0;
    
    for(int i = 0;i < k;i++) // 遍历k条边  最多不超过k条边
    {
        memcpy(backup,dist,sizeof dist); 

        for(int j = 0;j < m;j++) // 每个边都遍历一遍 
        {
            int a = tu[j].x,b = tu[j].y,w = tu[j].c;
            if(dist[b] > backup[a] + w) // 同dijkstra相对比来理解
               dist[b] = backup[a] + w; // 用点a来更新点b
        }
    }
    
    return dist[sy];
}

int main()
{
    cin >> n >> m >> k >> sx >> sy;
    
    for(int i = 0;i < m;i++)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        tu[i] = {a,b,c};
    }
    
    int t = bellman_ford();
    if(t > INF / 2) cout << "-1" << endl; // 如果存在负权边 无法到达 可能不是INF
    else cout << t << endl;
    
    return 0;
}

spfa
  1. 最短路

#include <bits/stdc++.h>

using namespace std;

const int N = 1010,INF = 0x3f3f3f3f,M = 20010;

int n,m,sx,sy;
int h[N],e[M],ne[M],idx,w[M];
int dist[N];
bool st[N];
queue<int> q;

void add(int a,int b,int c)
{
    w[idx] = c;
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

int spfa()
{
    memset(dist,0x3f,sizeof dist);
    dist[sx] = 0;
    q.push({sx});
    st[sx] = true;
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        st[t] = false; // 若存在其它更短路径 还会再次入队
        
        for(int i = h[t];i != -1;i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i]; // 发生重复加入之前会被st[]数组判掉
                if(!st[j])               //  因为 更新距离在if(!st[j])之上 不会加入队列 但如果距离较小的话 仍然会被更新
                {
                    st[j] = true;
                    q.push({j});
                }
            }
        }
    }
    
    return dist[sy];
}

int main()
{
    cin >> n >> m >> sx >> sy;
    
    memset(h,-1,sizeof h);
    
    for(int i = 0;i < m;i++)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c); add(b,a,c);
    }
    
    int t = spfa();
                                        // 注意此处对于 不存在路径的判断 
    if(t == INF) cout << "-1" << endl; // 因为每次更新都是用之前已经更新过的节点 如果不存在负环则不考虑其它情况
    else cout << t << endl; 
    
    return 0;
}
  1. 判断是否存在负环

#include <bits/stdc++.h>

using namespace std;

const int N = 2010,M = 10010;

int h[N],e[M],ne[M],idx,w[M];
int dist[N],cnt[N],n,m;
bool st[N];
queue<int> q;

void add(int a,int b,int c)
{
    w[idx] = c;
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

bool spfa()
{
    //memset(dist,0x3f,sizeof dist);  注: 理解 不需要初始化 及相应下面距离的更新
    //dist[1] = 0;
    for(int i = 1;i <= n;i++)
    {
        q.push(i);
        st[i] = true;
    }
    
    while(q.size())
    {
        int t = q.front();
        q.pop();
        
        st[t] = false;
        
        for(int i = h[t];i != -1;i = ne[i])
        {
            int j = e[i];
            if(dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if(cnt[j] >= n) return true;
                if(!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }
    
    return false;
}

int main()
{
    memset(h,-1,sizeof h);
    
    cin >> n >> m;
    for(int i = 0;i < m;i++)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c);
    }
    
    if(spfa()) puts("Yes");
    else puts("No");
    
    return 0;
}

Floyd

ACWing 854.Floyd求最短路

#include <bits/stdc++.h>

using namespace std;

const int N = 210,INF = 1e9;

int g[N][N],n,m;

void floyd()
{
    for(int k = 1;k <= n;k++) // k <= n 
      for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
          g[i][j] = min(g[i][j],g[i][k] + g[k][j]);
}

int main()
{
    int T;
    scanf("%d %d %d",&n,&m,&T);
    
    for(int i = 1;i <= n;i++)
      for(int j = 1;j <= n;j++)
        if(i == j) g[i][j] = 0;
        else g[i][j] = INF;
    
    while(m--)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        g[a][b] = min(g[a][b],c);
    }
    
    floyd();
    
    while(T--)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        if(g[x][y] > INF / 2) puts("impossible");
        else printf("%d\n",g[x][y]);
    }
    
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值