Home » Java » java – I am getting wrong answer for prime path problem can anyone help me in finding the issue?-Exceptionshub

java – I am getting wrong answer for prime path problem can anyone help me in finding the issue?-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

Problem Link: https://www.spoj.com/problems/PPATH/

Brief explanation of the problem,

1) Construct a graph with prime numbers between 1000 and 9999.
2) Add an undirected edge between two numbers 'a' and 'b', if they differ only by one digit. 
EX: 1033 and 1733 differ only by one digit.
3) In that graph we need to find the length of the shortest path from the given source to the given destination.

I have solved the above problem by constructing a graph using the prime number between 1000 and 9999, by connecting numbers that differ only by one digit. EX: 1033 and 1733 differ only by one digit.
I have used DFS along with memorisation to find the shortest path.
For some input i am getting wrong answer, 1 greater than the actual value, since there are 1000 nodes i can’t able to figure out the issue. It will be so helpful if someone help me to figure out the issue.

I know this problem can be solved by BFS, but i need to know what’s wrong with this problem.

test cases when the below program prints wrong answer

1
7573 9973

Actual answer : 4

My code output : 5

(I have found the actual answer by submitting a BFS approach to the problem and it got Accepted in SPOJ).

import java.util.*;
import java.lang.*;
import java.io.*;


class FireEscapeRoutes_FIRESC {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String[] args) throws Exception{
        int t = 1;
        while (t--!=0){
            int source = 7573;
            int destination = 9973;
            List<Integer> fourDigitPrimeNos = new ArrayList<>();

            for(int i=1001;i<=9999;i++){
                if(isPrime(i)){
                    fourDigitPrimeNos.add(i);
                    //System.out.println(i);
                }
            }
            Graph graph = new Graph(fourDigitPrimeNos.size());
            /*
             If two number 'a' and 'b' differ only by one digit then an edge is added.
             */
            for (int i=0;i<fourDigitPrimeNos.size();i++){
                for (int j=i+1;j<fourDigitPrimeNos.size();j++){
                    if(isSingleDistnace(fourDigitPrimeNos.get(i),fourDigitPrimeNos.get(j))){
                        graph.add(fourDigitPrimeNos.get(i),fourDigitPrimeNos.get(j));
                    }
                }
            }
            //System.out.println(graph.graph);
            Long minPath = graph.getShortestPath(source,destination);
            if(minPath!=Long.MAX_VALUE){
                System.out.println(minPath);
            }else{
                System.out.println("Impossible");
            }

        }
    }

    static boolean isSingleDistnace(int a, int b){
        String as = a+"";
        String bs = b+"";
        int ds = 0;
        for (int i=0;i<as.length();i++){
            if(!(as.charAt(i)==bs.charAt(i))){
                if(ds>=1){
                    return false;
                }
                ds++;
            }
        }
        if(ds==0){
            return false;
        }
        return true;
    }
    static boolean isPrime(int n){
        for (int i=2;i<=Math.sqrt(n);i++){
            if(n%i==0){
                return false;
            }
        }
        return true;
    }
}

class Graph{
    int noOfVertices;
    HashMap<Integer,List<Integer>> graph;
    Graph(int v){
        noOfVertices = v;
        graph = new HashMap<Integer,List<Integer>>();
    }
    void add(int u,int v){
        if (!graph.containsKey(u)){
            graph.put(u,new ArrayList<>());
        }
        if(!graph.containsKey(v)){
            graph.put(v,new ArrayList<>());
        }
        graph.get(u).add(v);
        graph.get(v).add(u);
    }

    Long getShortestPath(int start, int dest){
        HashMap<Integer,Long> visitedVsMinCost = new HashMap<>();
        Long min = Long.MAX_VALUE;
        min = getShortestPathUtil(start,dest,visitedVsMinCost);
        return min-1;


    }

    Long getShortestPathUtil(Integer start,Integer dest,HashMap<Integer,Long> visitedVsMinCost){

        if(start.equals(dest)){
            return 1l;
        }
        visitedVsMinCost.put(start, Long.MAX_VALUE);
        List<Integer> frnds = graph.get(start);
        Long min = Long.MAX_VALUE;
        for (Integer iThFrind:frnds){
            if(!visitedVsMinCost.containsKey(iThFrind)){
                Long shortestPathUtil = getShortestPathUtil(iThFrind, dest, visitedVsMinCost);
                //System.out.println(shortestPathUtil + " min " + min);
                min = Math.min(min, shortestPathUtil);

            }else {
                if(!visitedVsMinCost.get(iThFrind).equals(Long.MAX_VALUE)) {
                    min = Math.min(min, visitedVsMinCost.get(iThFrind)+1);
                }
            }
        }
        visitedVsMinCost.put(start,min);
        //System.out.println(min);
        if (min.equals(Long.MAX_VALUE)){
            return min;
        }
        return min+1;

    }
}

NOTE: This below Part is to explain why my code works on the situation mentioned by @c0der. Since i can’t able to comment more characters i am editting this question. To understand approach you can use this below part.

I can understand it is difficult to debug the code, so i try to explain my approach using the graph mentioned by @coder answer and
above code work fine in the scenario you mentioned.

Start = 1 and destination= 5, shortest path = 2 (1->4->5)

1) if DFS traverses through `1->2->3->4->5′ and reached the destination ‘5’ it return ‘1’ to the ‘4’th node.

2) now the ‘4’th node memorise the returned value ‘1’. (This means between 4 and 5, there is one node, including destination, excluding the source 4).

2.1) Then it returns ‘2’(1+1) to the ‘3’rd node. and ‘3’rd node memorise the value ‘2’. (This means between 3rd node and destination(5) node , there is 2 node, in the shortest path. including destination, excluding source 3

3) similiarly call will go back to ‘1’.

4) then ‘1’st node, calls ‘4’th node and see it is visited before, so it takes the memorized value of ‘4’ th node which is ‘1’ and it returns ‘2’ to ‘1’.

How to&Answers:

Debugging of the code posted is a long task.
However DFS is not the right tool for the job.
To visualize why DFS is not a good tool to find the shortest path consider the following simple graph:

enter image description here

If DSF happens to start by traversing nodes 1->2->3->4->5 the shortest path 1->4->5
will not be traversed because 4 is marked as visited.
This may be the reason why DFS along with memorisation is unable to find the shortest path.



Edit:
The following is a modified version of your code: it returns the actual shortest path found, if any.
This may help in debugging.
If finds the shortest path by performing DFS to traverse all possible paths and keeping the shortest one.
It is not optimized in the sense that if there are loops in the graph it may recalculate a path that has already been calculated before. You may want to add memorization of calculated pathes to make it more efficient.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

class FireEscapeRoutes_FIRESC {

    public static void main(String[] args) throws Exception{

        List<Integer> fourDigitPrimeNos = new ArrayList<>();

        for(int i=1001; i<=9999; i++){
            if(isPrime(i)){
                fourDigitPrimeNos.add(i);
            }
        }

        Graph graph = new Graph();
        /*
             If two number 'a' and 'b' differ only by one digit then an edge is added.
         */
        for (int i=0;i<fourDigitPrimeNos.size();i++){
            for (int j=i+1;j<fourDigitPrimeNos.size();j++){
                if(isSingleDistnace(fourDigitPrimeNos.get(i),fourDigitPrimeNos.get(j))){
                    graph.add(fourDigitPrimeNos.get(i),fourDigitPrimeNos.get(j));
                }
            }
        }

        int source = 1033; 
        int destination = 8179;  //expected 6 : 1033  1733  3733  3739  3779  8779  8179

        /* more test cases
        int source = 7573;
        int destination = 9973; //expected 5

        int source = 1373;
        int destination = 8017; //expected 7

        int source = 1033;
        int destination = 1033; //expected 1
        */
        List<Integer> shortestPath = graph.getShortestPath(source,destination);

        if(shortestPath != null){
            System.out.println("\nPath Found :"+ shortestPath);
            System.out.println("Path length: "+shortestPath.size());
        }else{
            System.out.println("Impossible");
        }
    }

    static boolean isSingleDistnace(int a, int b){
        String as = a+"";
        String bs = b+"";
        int ds = 0;
        for (int i=0;i<as.length();i++){
            if(!(as.charAt(i)==bs.charAt(i))){
                if(ds>=1)
                    return false;
                ds++;
            }
        }
        if(ds==0)   return false;

        return true;
    }

    static boolean isPrime(int n){

        if (n <= 1) return false; //make sure it is positive

        for (int i=2;i<=Math.sqrt(n);i++){
            if(n%i==0)
                return false;
        }
        return true;
    }
}

class Graph{

    private final HashMap<Integer,List<Integer>> graph;
    //measure time and print out some progress indication
    private static long startTime, printime;
    private static long HEART_BEAT = 15000;

    Graph(){
        graph = new HashMap<>();
    }

    void add(int u,int v){
        if (!graph.containsKey(u)){
            graph.put(u,new ArrayList<>());
        }
        if(!graph.containsKey(v)){
            graph.put(v,new ArrayList<>());
        }
        graph.get(u).add(v);
        graph.get(v).add(u);
    }

    List<Integer> getShortestPath(int start, int dest){

        System.out.print("Working ");
        startTime = System.currentTimeMillis();

        List<Integer> path = new ArrayList<>();
        path = getShortestPathUtil(start,dest, Integer.MAX_VALUE, path);

        System.out.println("\n run time in minutes " + (double) (System.currentTimeMillis() - startTime) /60000);
        return path;
    }

    List<Integer> getShortestPathUtil(int start,int dest,int minLengthFound, List<Integer> path){

        if(System.currentTimeMillis() - printime >= HEART_BEAT ){
            System.out.print(".");
            printime = System.currentTimeMillis();
        }

        if(path.contains(start)) return null; //prevent loops

        path.add(start);
        if(start == dest) return path;
        //stop traverse if path is longer than the shortest one found earlier
        if(minLengthFound != Integer.MAX_VALUE && path.size() >= minLengthFound) return null;

        List <Integer> keepShortestPathFound = null;
        for (int neighbor : graph.get(start)){

            if(path.contains(neighbor)) {
                continue;
            }

            List<Integer> shortestPathFromNeighbor = getShortestPathUtil(neighbor, dest, minLengthFound, new ArrayList<>(path));
            if(shortestPathFromNeighbor != null && shortestPathFromNeighbor.contains(dest) &&
                    shortestPathFromNeighbor.size() < minLengthFound){
                keepShortestPathFound = shortestPathFromNeighbor;
                minLengthFound = shortestPathFromNeighbor.size();
            }
        }

        return keepShortestPathFound;
    }
}