Shortest path problem

Posted by oddyseys on Fri, 18 Feb 2022 21:17:43 +0100

Shortest path problem

Sequence: shortest path problem: find the weight and minimum path length from one point to another in the graph. (the following n indicates the number of points and m indicates the number of sides)

I Naive dijkstra algorithm

Principle: dijkstra algorithm is based on breadth first search and expands each point in turn according to breadth first search from the center of a source point. It adopts the idea of greed. We can use array dist [] to represent the distance of the shortest path from the source point to each point. Then solve according to the following steps:

  1. Initialization: set a dist[N] array to store the distance from each point to the source point, set X as the source point, initialize dist [] all data to infinity, and the code is expressed as memset(dist, 0x3f, sizeof dist); For the source point itself, dist[x]=0.
  2. Initialization of initial conditions: for dijkstra algorithm, we use G [] [] to represent the distance between each two points. G is initialized to infinity, memset(g,0x3f,sizeof g); Update the minimum distance between each two points according to the data given in the topic. Note the difference between directed graph and undirected graph.
  3. Update: we can use A set A to represent the point whose shortest distance has been determined, use t to represent the point that is no longer in set A and closest to the source point, and use t to update the shortest distance from other points in A to the source point. In the coding process, we can use A bool array st [] to simulate set A. You can update other points with the source point for the first time, and then update other points with the point with the smallest distance from the source point in turn. You can get the answer by updating it up to n times.

Example analysis: find the shortest distance from point 1 to point 4

Step 1: dist[1]=0,dist[2]=INF,dist[3]=INF,dist[4]=INF; (INF indicates positive infinity)

Step 2: g[1][2]=3,g[2][3]=3,g[2][4]=5,g[3][4]=1; Other INF [];

Step 3: update:

(1) Update other points with source point 1: dist[2]=dist[1]+g[1][2]=3; Point 1 enters set A, st[1]=true;

(2) The midpoint 2 of other points is closest to the source point 1, t=2; Use point 2 to update the shortest distance from other points to the source point, dist[3]=dist[2]+g[2][3]=6,dist[4]=dist[2]+g[2][4]=8; Point 2 enters set A, st[2]=true;

(3) The analysis above shows that t=3, dist [3] + G [3] [4] = 7 < dist[4]; So dist[4] is updated to 7,st[3]=true;

(4) For the last update, point 4 is updated, and the results of other points remain unchanged, st[4]=true; So far, the update is over, and the answer dist[4]=7

It can be concluded that each update can enter a point into set A. therefore, when the problem is solved, we can calculate the shortest distance between all points and the source point at most n updates. You can get the answer.

II Heap optimized dijkstra algorithm

Principle: for graphs with too many edges, the simple dijkstra algorithm will timeout, so it must be optimized. Here we the data structure heap optimization (priority_queue). In essence, this method exchanges more space for less time, and achieves the optimization of time complexity. Here, we use the adjacency table to store the weights of edges and edges. The edge adding code is as follows. For details, please see 1 AcWing ; We use priority_ Queue < PII, vector, greater > heap (PII is pair < int, int >); Access the distance from each point to the source point and its own number, and pair is sorted by the first key value. Each time the point on the top of the heap is selected (at this time, the point on the top of the heap must be the point with the shortest distance from the source point), it is used to update the distance between other points and the source point. The points that have been used to update other points enter the set A (st[i]=true), indicating that the point with the shortest distance has been determined. Until the heap is empty, the shortest distance from all points to the source point is updated.

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

III Bellman Ford algorithm

Principle: traverse n times, and traverse all edges from small to large each time. Here, the structure is used to store edges and edge weights.

Here is the reason why you need to traverse n times:

In the worst case, the source point to the target point will pass through n-1 edges at most. At worst, only one point will be updated each time. Only n traversals can ensure the shortest distance from the target point to the source point.

The solution does not allow negative weight loops. The algorithm can solve the problem of finding the shortest path with the limit of the number of edges. When the distance from the source point is no more than k edges, traverse K times, and find the shortest distance from two points on each edge to the source point from small to large each time. The following is an example to analyze the bellman Ford algorithm.

Example analysis: find the shortest distance between point 1 and point 4

Step 1: store all Edges into the structure Edges;

Step 2: update:

First traversal:

Edge (1,2), dist[2]=dist[1]+w=3;

Edge (2,3), dist[3]=dist[2]+w=6;

Edge (2,4), dist[4]=dist[2]+w=8;

Edge (3,4), dist[4]=dist[3]+w=7;

It can be seen that the shortest distance can be obtained through the first traversal of this problem.

IV spfa algorithm

Principle: this algorithm is an optimization of Bellman Ford algorithm. When we use Bellman Ford algorithm, every edge needs to be updated, but not necessarily every edge will be updated. Here, we use queue optimization to put the source point into the queue. As long as the queue is not empty, each update will put the point into the queue as long as the distance from the point to the source point becomes smaller and the point is not in the queue; Finally, the distance from each point to the source point must be the shortest distance. If the queue is empty, exit the update. Here, a bool array st [] is used to indicate whether the point is in the queue, st[i] is false, not in, and st[i] is true; The algorithm is not available in most cases of negative loop complexity. The following is an example to analyze the spfa algorithm.

Example analysis: find the shortest distance between point 1 and point 4

Step 1: use the adjacency table to store the information of each edge;

Step 2: enter source point 1 into the queue;

Step 3: update:

Point 1: exit the team at point 1, st[1]=false;dist[2]=3,2 into the queue, st[2]=true;

Point 2: leave the team at point 2, st[2]=false;dist[3]=6,dist[4]=8,3 and 4 join the team, st[3]=true,st[4]=true;

Point 3: leave the team at point 3, st[3]=false;dist[4]=7,4 is already in the team and does not need to join the team;

Point 4: exit the queue at point 4, St [4] = false, and the queue is empty.

When the update is completed, dist[4]=7 is obtained;

V floyd algorithm

Principle: this algorithm is a dynamic programming algorithm, which is easier to understand than the previous algorithms. It can calculate the shortest distance between any two points. The disadvantage is that the time complexity is too high to solve the problem of negative weight loop. We use the adjacency matrix d [] [] to represent the distance between each two points, and d[i][j] represents the shortest distance between I and j, then I to J can be directly from I to j, or the shortest distance from I to j through several points. The specific steps are as follows:

  1. initialization:

        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                if (i == j) d[i][j] = 0;
                else d[i][j] = INF;
    

    If i==j, d[i][j]=0; otherwise, all are initialized to INF.

  2. Initialization of topic initial condition: initialize d[i][j] of the distance between each two points according to the topic condition;

  3. Update: cycle n times, each time d[i][k]+d[k][j] indicates the shortest distance from I to j after K. if it is less than d[i][j], update d[i][j];

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

Example analysis: find the shortest distance between any two points

The first step is INF [3] [D = 3] [2, and the rest is INF [3] [D = 3] [4] [D = 3] [2];

Step 2: update:

k=1, unchanged

k=2,dist[1][3]=dist[1][2]+dist[2][3]=6,dist[1][4]=dist[1][2]+dist[2][4]=8;

k=3,dist[1][4]=dist[1][3]+dist[3][4]=7;

k=4, unchanged;

Finally, the distance between each two points is the shortest distance.

Vi Some topics

Question 1: messenger

  1. analysis:

    We can abstract the headquarters and posts as points, and the time of sending messages between posts and headquarters or posts can be regarded as the edge right between two points.

    The topic asks us to find out the shortest time needed to complete the whole process of sending letters. Among them, to complete the whole process: the headquarters is required to reach other posts at point 1, that is, other points.

    Therefore, we require the shortest sending time, only the longest time from point 1 to other points, that is, the maximum value of the shortest distance from each point to point 1.

  2. Selection algorithm: it can be seen that the data of this problem is not large, so you can choose the simplest floyd algorithm

  3. Code implementation:

#include <iostream>
#include <cstring>
using namespace std;
const int =110;
int g[N][N],INF=0x3f3f3f3f;//Storing edges with adjacency matrix
int n,m;

int main()
{
    cin>>n>>m;
    memset(g,0x3f,sizeof g);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        g[a][b]=g[b][a]=min(g[a][b],c);//The title is given as an undirected edge
    }
    for(int k=1;k<=n;k++)
    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]);
    }//floyd algorithm
    int res=0;
    for(int i=2;i<=n;i++)
    {
        if(g[1][i]==INF) //If a post cannot be reached, output - 1
        {
            cout<<-1;
            return 0;
        }
        res=max(res,g[1][i]);//Taking the maximum value of the shortest distance from all points to the source point is the problem
    }
    cout<<res;
}

Question 2: sweet butter

  1. Analysis: the meaning of the topic is to find a point so that the sum of the shortest distances from other points to the point is the smallest. Take the pasture as the point on the map, and the road between the pastures is the edge on the map. So you only need to find the shortest path to other points once for each point.

  2. Select algorithm: we can select spfa() algorithm for the range of data here. The total time complexity is O(nm);

  3. Code implementation:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=810,M=3000,INF=0x3f3f3f3f;
    int h[N],e[M],ne[M],w[M],idx;
    int dist[N];
    bool st[N];
    int n,p,c;
    int id[N];//Store the pasture number of each cow
    void add(int a,int b,int c)//Store information for every two ranches
    {
        e[idx]=b;
        ne[idx]=h[a];
        w[idx]=c;
        h[a]=idx++;
    }
    int spfa(int x)
    {
        memset(dist,0x3f,sizeof dist);
        dist[x]=0;
        queue<int> q;
        q.push(x);
        st[x]=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];
                    if(!st[j])
                    {
                        st[j]=true;
                        q.push(j);
                    }
                }
            }
        }
        int res=0;
        for(int i=1;i<=n;i++)
        {
            int j=id[i];
            if(dist[j]==INF) return INF;//This point cannot reach all points and returns to infinity.
            res+=dist[j];
        }
        return res;
    }
    int main()
    {
        memset(h,-1,sizeof h);
        cin>>n>>p>>c;
        for(int i=1;i<=n;i++) cin>>id[i];
        while(c--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
            add(b,a,c);//Undirected edge
        }
        int res=INF;
        for(int i=1;i<=p;i++) res=min(res,spfa(i));//Solve the sum of the shortest distance from each point to other points and take the minimum value.
        cout<<res;
    }
    

Question 3: minimum cost

  1. Analysis: in this question, we can take each person as the point in the figure, and the handling fee required by each two people to transfer money as the edge weight in the figure. The question requires us to find out that the transfer from point (person) a to point (person) B makes B have 100 yuan, and how much does the initial a need at least? We can think in reverse. We just need to find the product res of the maximum distance from point B to point a, and then divide 100 by res to get the minimum cost of A.

  2. Selection algorithm: choose the simple version of dijkstra algorithm demonstration here. Note the edge storage here.

  3. Code implementation:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e3+10;
    
    double g[N][N],dist[N];;//The title requires 8 decimal places. double is used here
    int n,m;
    int A,B;
    bool st[N];
    
    double dijkstra()
    {
        dist[A]=1;
        for(int i=0;i<n;i++)
        {
            int t=-1;
            for(int j=1;j<=n;j++)
                if(!st[j]&&(t==-1||dist[t]<dist[j]))
                t=j;
            st[t]=true;
            for(int j=1;j<=n;j++)
            dist[j]=max(dist[j],dist[t]*g[t][j]);//Find the maximum distance product and change '+' to '*'
        }
        return dist[B];
    }
    int main()
    {
        cin>>n>>m;
        while(m--)
        {
            int a,b,c;
            cin>>a>>b>>c;
            double z=(100.0-c)/100;//The side weight deposited here is given in the double title as the deduction percentage. What we deposit is the remaining percentage after deducting the deduction, which is convenient for calculation.
            g[a][b]=g[b][a]=max(g[a][b],z);
        }
        cin>>A>>B;
        double res=dijkstra();
        printf("%.8lf",100/res);
    }
    

VII Appendix (four solution templates, reproduced from AcWing)

1. Naive dijkstra algorithm

//The time complexity is O (N2 + m), where n represents the number of points and M represents the number of sides

int g[N][N];  // Store each edge
int dist[N];  // Store the shortest distance from point 1 to each point
bool st[N];   // Whether the shortest circuit of each storage point has been determined

// Find the shortest circuit from point 1 to point n. if it does not exist, return - 1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // Look for the point with the smallest distance among the points where the shortest circuit has not been determined
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // Update the distance of other points with t
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t·	] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

2. Heap optimized version dijkstra

//Time complexity O(mlogn), n represents the number of points, and m represents the number of edges

typedef pair<int, int> PII;

int n;      // Number of points
int h[N], w[N], e[N], ne[N], idx;       // The adjacency table stores all edges
int dist[N];        // Store the distance from all points to point 1
bool st[N];     // Is the shortest distance to store each point determined

// Find the shortest distance from point 1 to point n. if it does not exist, return - 1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first storage distance, second storage node number

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

3. Bellman Ford algorithm

//Time complexity O(nm), n represents the number of points, and m represents the number of edges

int n, m;       // n represents the number of points and m represents the number of sides
int dist[N];        // dist[x] stores the shortest distance from 1 to X

struct Edge     // Edge, a represents the point, b represents the entry point, and w represents the weight of the edge
{
    int a, b, w;
}edges[M];

// Find the shortest distance from 1 to n. if you can't go from 1 to n, return - 1.
int bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    // If the n-th iteration still relaxes the trigonometric inequality, it indicates that there is a shortest path with a length of n+1. According to the drawer principle, there are at least two identical points in the path, indicating that there is a negative weight loop in the graph.
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (dist[b] > dist[a] + w)
                dist[b] = dist[a] + w;
        }
    }

    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];
}. 

4.spfa algorithm

//Time complexity: O(m) in the average case, O (nm) in the worst case, n represents the number of points, and M represents the number of edges

int n;      // Total points
int h[N], w[N], e[N], ne[N], idx;       // The adjacency table stores all edges
int dist[N];        // Store the shortest distance from each point to point 1
bool st[N];     // Stores whether each point is in the queue

// Find the shortest distance from point 1 to point n. if you can't walk from point 1 to point n, return - 1
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = 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];
                if (!st[j])     // If j already exists in the queue, there is no need to insert j repeatedly
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

5.floyd algorithm

//The time complexity is O (n ^ 3), and N represents the number of points
//initialization:
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// After the algorithm, d[a][b] represents the shortest distance from a to B
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

Reference:

1.AcWing

Topics: Algorithm dijkstra