roycf123's blog

By roycf123, history, 17 months ago, In English

Recently, I was solving the Graphs section of the CSES problemset and encountered this problem. I used dijkstra algorithm to solve it.

I generally use the template from here, but in this question, this implementation gave me WA. When I used another implementation using visited array, it gave AC.

WA code
AC Code

Naturally, I adopted the visited array approach and discarded the one before.

Now, using the visited array approach on this, I encountered WA again.

WA Code

Now I am very confused about what template to use for dijkstra, being a newbie to graphs. I would highly appreciate if someone helps.

  • Vote: I like it
  • +8
  • Vote: I do not like it

»
17 months ago, # |
Rev. 3   Vote: I like it 0 Vote: I do not like it

Even I had such confusing moments with Dijstra's Algorithm once. Could you please check and tell me whether the below implementation works? Even I would like to know whether it is accurate in all cases.

#include <bits/stdc++.h>
using namespace std;

//Adjecency list representation of an undirected graph
class Graph {
    int n;
    vector<vector<pair<int,int>>> gp;
    public:
    Graph(int n) {
        this->n=n;
        gp.resize(n);
    }
    void addedge(int a, int b, int w) {
        gp[a].push_back(make_pair(b,w));
        gp[b].push_back(make_pair(a,w));
    }
    vector<pair<int,int>> adj(int c) {
        return gp[c];
    }
};

//Function to calculate the shortest path and return the distance to each vertex from the starting vertex
vector<int> DijkstraShortestPath(int n, vector<vector<int>> edges, int start) {
    Graph g(n);
    //Creating the graph from the vector of vectors with each vector containing the two vertices and the weight of the edge between them
    for(auto i:edges) {
        g.addedge(i[0]-1,i[1]-1,i[2]);
    }
    vector<bool> removed(n,false);
    vector<int> dist(n,-1);
    //z variable to keep track of the number of vertices removed from the priority queue
    int z=n;
    auto cmp = [](pair<int,int>& a, pair<int,int>& b) {
        return a.second>b.second;
    };
    //Defining a priority queue of pairs with each pair containing the vertex and its assisgned weight at that step
    priority_queue<pair<int,int>,vector<pair<int,int>>,decltype(cmp)> pq(cmp);
    dist[start-1]=0;
    pq.push(make_pair(start-1,0));
    while ((!pq.empty())&&(z)) {
        pair<int,int> k=pq.top();
        pq.pop();
        if(!removed[k.first]) {
            z--;
            removed[k.first]=true;
            for(pair<int,int> i:g.adj(k.first)) {
                if(dist[i.first]!=-1) {    //If the vertex had previously been updated
                    if(k.second+i.second<dist[i.first]) {
                        dist[i.first]=k.second+i.second;
                        pq.push(make_pair(i.first,k.second+i.second));
                    }
                }
                else {    //If the vertex has never been updated
                    dist[i.first]=k.second+i.second;
                    pq.push(make_pair(i.first,k.second+i.second));
                }
            }
        }
    }
    return dist;
}
  • »
    »
    17 months ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    I would appreciate if you write the Dijkstra function taking adjacency list as input. Would be really easier to test it. Right now, it is a little difficult to read or use for me.

    Nevertheless, if you want to test it yourself, create an account in cses.fi and submit your code (you would need to incorporate parent assigning as well).

»
17 months ago, # |
Rev. 2   Vote: I like it +2 Vote: I do not like it

There are a couple of things going on. Let's focus on the first problem you were solving, which seems to be Flight Discount.

Both of the dijkstra implementations are correct, but the first one contains a bug (and thus it gets WA). This bug is not present in the original implementation from CP-Algorithms.

In your function dijkstra (and also in dijkstra1)

st.erase({d1[child],child});
if(d1[child]>d1[node]+wt){
    d1[child]=d1[node]+wt;
    st.insert({d1[child],child});
}

should have the top two lines swapped:

if(d1[child]>d1[node]+wt){
    st.erase({d1[child],child});
    d1[child]=d1[node]+wt;
    st.insert({d1[child],child});
}

In the first (incorrect) piece of code, the node child may just get completely deleted from the set of possible next nodes without ever visiting it, which is obivously incorrect.

But why did the implementation of dijkstra get WA in the second problem? I can assure you that the mistake is not in the implementation of dijkstra. You got WA, because dijkstra itself cannot solve that problem efficiently. Here is a test case where your code fails:

Test Case
Picture of the Graph

Due to the fact that pairs in c++ are sorted in order of first element and then second, and the second element of each pair is the node index, dijkstra will first go to node $$$4$$$ and to node $$$7$$$ from there, setting the parent of $$$7$$$ as $$$4$$$. After that, dijkstra will find the path $$$1 \rightarrow 3 \rightarrow 6 \rightarrow 7$$$ and because it is longer than $$$1 \rightarrow 4 \rightarrow 7$$$, the parent of $$$7$$$ gets set to $$$6$$$. Last, dijkstra will find the path $$$1 \rightarrow 2 \rightarrow 5 \rightarrow 4$$$ and set the parent of $$$4$$$ to $$$5$$$. But since $$$4$$$ got visited already, the algorithm doesn't update $$$7$$$'s parent back to $$$4$$$.

In order to make dijkstra work in this problem, you would have to visit every node again if the distance got updated, making the algorithm run in $$$O(m^2 \log n)$$$. This is the same reason as to why dijkstra doesn't work with negative weights.

Instead of dijkstra, this problem is meant to be solved with topological sorting and dp on DAG.