Блог пользователя TomDev

Автор TomDev, история, 3 месяца назад, По-английски

I had checked on some websites and they said that when Dijkstra went through a node, it marked that node as optimized and never visit again. But now, I implement Dijkstra with priority queue and compare by checking if distance[v] > distance[u] + w. By that way, it will just get the most optimized way for a node without getting trouble with negative weights.

Here's an example of my Dijkstra code to attempt High Score problem on CSES. My code uses Dijkstra to find the optimal path to the final path and also using the SPFA's trick (count if node is visited more than n times).

My code got Accepted on most of the testcases (except the only one testcase that got Wrong Answer because it detects the wrong cycle — and I don't know why)

My attempt's verdict

Can someone help me explain this? I know that I'm wrong somewhere but I'm so confused. Thank you!

  • Проголосовать: нравится
  • +4
  • Проголосовать: не нравится

»
3 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

In case of a negative cycle, it will run but give wrong answer since we can keep revolving around negative cycle to minimize distance and then get to destination ideal answer is -INF in this case, incase of negative weights but not negative cycle it will miss on the optimal path, since there could be a case that A -> C takes 2 units and A -> B takes 4 units but B ->C takes -3 units so it will miss the 1 unit shortest dist for C.

  • »
    »
    3 месяца назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    In case of the negative cycle, I tried SPFA trick so I think that would be possible to detect the cycle and exit early.

    With your example of the negative weight. My code will process:

    Process A: dis[A] = 0, dis[B] = 4, dis[C] = 2 (now B and C are in priority queue)

    Process the one with least weight (C): none (now priority queue left with B)

    Process next one with least weight (B): dis[C] (2) > dis[B] (4) - 3 so it update the distance: dis[C] = 1.

    So it still can find the optimal way.

    • »
      »
      »
      3 месяца назад, скрыть # ^ |
      Rev. 2  
      Проголосовать: нравится 0 Проголосовать: не нравится

      Ya so the SPFA actually solves the issue of Negative weight but not negative cycle,if the cycle itself is negative then it will keep moving in circle since everytime it visits any cyclic node it will have a decreased distance eg: — A -> B = 3 , B->C = -3 and C->A = -4, now A is 0 at start but when it reaches A again the dist is -4 so it updates now again when it reaches A it becomes -8 so on and so forth, so it becomes an infinite loop. Dijkstra wouldnt run into infinite loop since we dont visit a node twice but it fails for negative edges since it misses optimal Path, SPFA corrects it by visiting a node again but then it goes into a infinite cycle due to negative cycle.

      • »
        »
        »
        »
        3 месяца назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится

        Ok, so can we use SPFA trick on Dijkstra to know that it is a negative cycle and how about the graph with no negative cycles but negative weights?

        • »
          »
          »
          »
          »
          3 месяца назад, скрыть # ^ |
           
          Проголосовать: нравится 0 Проголосовать: не нравится

          Its actually the opposite, u can use SPFA for non negative cycles, since it finds the the optimal path after going through all paths (unlike dijkstra who goes only once per node). Incase of negative cycle u can search up bellman ford algorithm, it relaxes nodes n-1 times to get optimal way we do it once more to check if there is a negative cycle. U can use SPFA for negative cycles as well but u need to do additional modifications (that i never did so no idea here) to detect if it is falling into a infinite loop.

    • »
      »
      »
      3 месяца назад, скрыть # ^ |
      Rev. 2  
      Проголосовать: нравится 0 Проголосовать: не нравится

      Actually, your code is not Dijkstra, but rather a classic false optimization of SPFA, which has an exponential complexity assuming no constraint on edge weight, but it seems that the problem test data did not try to hack this solution.

      The reason why it gives WA is probably simply just because you set the distance of $$$n$$$ to $$$-1$$$ without checking if node $$$n$$$ is actually reachable by the nodes on the negative weight cycle.

      • »
        »
        »
        »
        3 месяца назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится

        You are right, it is not Dijkstra. But I've checked if the nodes on the negative weight cycle is reachable by n and even if 1 is reachable to them with the vis and vis2 bitset.

»
3 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

the problem is from cnt[v.u] .

when i removed it, i got AC on that testcase but RTE and TLE on the rest of the tests.

  • »
    »
    3 месяца назад, скрыть # ^ |
    Rev. 2  
    Проголосовать: нравится +1 Проголосовать: не нравится

    Also why are you using dijkstra to find the longest path.

    Dijkstra's algorithm cannot be used in graphs with negative weights.

    • »
      »
      »
      3 месяца назад, скрыть # ^ |
       
      Проголосовать: нравится 0 Проголосовать: не нравится

      That's what I'm asking. Why it can't be used in negative weights graph? As I described on the top comment. I can't find an example that it can't be used. I added that cnt array just to copy the SPFA trick on counting the node visiting times.

      And I do High Score with Dijkstra with the testing purpose to see if Dijkstra is good on negative weights and find out why

      • »
        »
        »
        »
        3 месяца назад, скрыть # ^ |
         
        Проголосовать: нравится -13 Проголосовать: не нравится

        dijkstra geedily gives a distance (dist[node]) once a node is visited . That distance never changes and assumed as the minimum path to the source.

        negative weights break that assumption .

        You can also think of it like this. the longest path problem is NP-Hard, dijkstra is P. so dijkstra can't solve longest path problem.

        • »
          »
          »
          »
          »
          3 месяца назад, скрыть # ^ |
           
          Проголосовать: нравится +27 Проголосовать: не нравится

          You can also think of it like this. the longest path problem is NP-Hard, dijkstra is P. so dijkstra can't solve longest path problem.

          This is wrong. The longest simple path is NP-Hard. In this problem we are allowed to visit the same vertex or use the same edge multiple times, and it is very much solvable in polynomial time.

      • »
        »
        »
        »
        3 месяца назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится

        if you can solve that problem with dijkstra then

        P = NP
        

        and you get 1 Million dollars

»
3 месяца назад, скрыть # |
 
Проголосовать: нравится 0 Проголосовать: не нравится

it will run INF times that's why it's no suitable for negative weights

»
3 месяца назад, скрыть # |
 
Проголосовать: нравится +5 Проголосовать: не нравится

Dijkstra works on the assumption that the minimum distance in the priority queue will always increase (or stay the same) with time. But having negative edges breaks this assumption.

  • »
    »
    3 месяца назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    but it can always update that node back if I use priority_queue instead of making a visit array. So it can be updated many times when the path is more optimized (unless it has a negative cycle). After some research I see that Dijkstra gonna do some backtrack $$$2^n$$$ approach on some "Killer" tests. But I can't find any simple test that counter my Dijkstra

»
3 месяца назад, скрыть # |
Rev. 8  
Проголосовать: нравится 0 Проголосовать: не нравится

Actually, you can solve it using Dijkstra like structure, but only if you understand the logic behind the Bellman Ford

code: MY AC CODE O(n²) also you can use priority queue but time will be O(n² log n²)

»
3 месяца назад, скрыть # |
 
Проголосовать: нравится +64 Проголосовать: не нравится

Consider a directed graph like this (v u cost, C is some big number):

Spoiler

In words: vertices come in groups of 3, it is very expensive to go from the first vertex in the group to the second, but then from the second to the third is negative s.t. going from the first to the third is actually $$$-2^k$$$, but you need to go through the second vertex. And from both the first and the third there is an edge of weight $$$1$$$ to the start of the next group.

The graph is clearly acyclic, since we only have edges from vertices with smaller ids to vertices with bigger ids; so there are no negative cycles (because there are no cycles). When you run your dijkstra from vertex $$$1$$$, you will update the distance to vertex $$$4$$$ and to vertex $$$2$$$. Then you will proceed from vertex $$$4$$$ with the rest of the graph, and all distances generated will be smaller than $$$C$$$, so you'll have to run the full traversal from vertex $$$4$$$ before processing vertex $$$2$$$. But then you will reach vertex $$$3$$$, which will in turn update vertex $$$4$$$ with a much smaller distance, so you'll have to run the whole thing from $$$4$$$ again.

Thus each new group makes you run dijkstra on the rest of the graph twice, which means that number of vertices visited will be of order $$$O(2^{n/3})$$$. $$$n$$$ is just $$$90$$$ because I need exponentially big weights to construct the example (and actually each time you process a vertex it means that the distance to it was decreased, and if the distances are integers that would mean that each vertex is processed at most $$$O(nW)$$$ times (where $$$W$$$ is bound on absolute value of edge weight) before you can establish that there is a negative cycle), but it is exponential in $$$n$$$.


If I want to be pedantic (and I do): this is not Dijkstra. The whole concept of Dijkstra is that once we decide to process a vertex, we are sure it has the correct distance now, and we will never have to process it again. It's a greedy algorithm, and it can be proven for non-negative weights. It is incorrect for negative weights (use the same example as above, except once we go through the rest of the graph from vertex $$$4$$$ we can't change it, so we wouldn't be able to go through vertex $$$3$$$ further).

  • »
    »
    3 месяца назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    Thank you so much! This is what I'm asking for. Also thanks everybody for helping me!! I think Dijkstra gonna help me pass some testcases in the future lol (if I'm lucky)