0

I need to output the shortest directions from 'P' to 'G'. I have used BFS for to implement it. The problem is as shown below:

Input: 'N' dimension maze '#' - Wall 'G' - Ghost 'P' - Pacman

//Input Maze
8
########
  #    #
# # ## #
# #  #G#
#P   ###
#### # #
#G   #G#
########

Output: directions in N/S/W/E
EX) E E E S S W W W

I keep getting time limit exceeded for my code. I can't pinpoint exactly where it may be the problem. My code is as follows:
I made a 'Point' class for the directions and the coordinates.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Maze{
    static char[][] map;
    static boolean[][] visited;
    static int[] dr = {1,-1,0,0};
    static int[] dc = {0,0,-1,1};
    static String[] compass = {"S","N","W","E"};
    static int cur_x, cur_y, cur_dist, N;
    static String nswe = "";
    static String curnswe = "";
    static String oldDir, dir;
    static int sx, sy;
    static int shortestDist;
    
    public static class Point{
        int x;
        int y;
        int dist;
        String ns;
        String dir;
        
        public Point(int x, int y, int dist, String oldDir, String dir){
            this.x = x;
            this.y = y;
            this.dist = dist;
            if("".equals(oldDir)) {
                this.ns = dir;
            }
            else { this.ns = oldDir + " " + dir; 
            
            }
            }
    }
    

This is the BFS method():

    public static void bfs(int x, int y, int dist, String oldDir, String dir){
        Queue<Point> q = new LinkedList<Point>();
        
        q.offer(new Point(x,y,dist,oldDir,dir));
        
        while(!q.isEmpty()){
            
            Point cur = q.poll();
            cur_x = cur.x;
            cur_y = cur.y;
            cur_dist = cur.dist;
            curnswe = cur.ns;
         
            nswe = curnswe;
            
            if(map[cur_x][cur_y]=='G') {
                
                return;
                
            }
                   
                       
            for(int dir1 = 0; dir1<4; dir1++){
                int r = cur_x + dr[dir1];
                int c = cur_y + dc[dir1];
                String d = compass[dir1];
                
                if(r >= 0 && c >= 0 && r < N && c < N){
                    
                    if(map[r][c]!='#')
                        if(!visited[r][c]){
                        
                        q.offer(new Point(r,c,cur_dist+1,curnswe,d));
                        visited[r][c] = true;
                    }
                }
            }
        }
        q.clear();
        
    }
    

This is the main part of the code where I input the maze, solve (BFS), and print out the directions.

public static void main(String[] args) throws IOException{
        
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter output = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st = new StringTokenizer(input.readLine());
        
        N = Integer.parseInt(st.nextToken());
        
        int dist = 1;
        
        map = new char[N][N];
        visited = new boolean[N][N];
        
        for(int i=0;i<N;i++){ 
            
            String line = input.readLine();
            
            for (int j=0; j<N; j++) {
                char t = line.charAt(j);
      
                if(t=='P') {sx=i; sy=j;}
                
                map[i][j] = line.charAt(j);
                
            }
        }
        
        
            bfs(sx,sy,dist,"","");
        
            output.write(nswe + " ");
            
            output.flush();
    }

}

  • You can increase the likelihood of getting an answer by providing a [mre]. As it stands, people would have to repair the code into something compilable and runnable and that work shouldn't be necessary. –  Apr 04 '21 at 09:01
  • 1
    The less fixable problem is that TLE usually means that you're getting a timeout from some online checker for unknown input and the input you provided does not actually reproduce the problem. Am I interpreting that correctly? –  Apr 04 '21 at 09:02
  • If the input you have shown does reproduce the problem, just take a debugger to the program and observe what it's doing. –  Apr 04 '21 at 09:06
  • @dratenik I will keep that in mind next time! and yes, I submitted it to an online judge. I can't see the input that the code is having an TLE of. – Hyung-Jo Kwon Apr 04 '21 at 09:22
  • Why wait for next time? You can [edit] your question to improve it. –  Apr 04 '21 at 09:28
  • I will do it now. So you recommend debugging it to see what my program is doing? – Hyung-Jo Kwon Apr 04 '21 at 09:35
  • If you don't know what the problematic input is, the first thing is to try out different pathological (no path? multiple pacmans? etc.) and/or large inputs and see which one does something unexpected. –  Apr 04 '21 at 09:45
  • I have edited the code. Does this suffice as a minimal reproducible example? I have tried all the possibilities that you have mentioned. My code outputs it fine. – Hyung-Jo Kwon Apr 04 '21 at 09:55
  • The problem is the O(N^2) time and space that you spend keeping track of the path to every vertex. https://stackoverflow.com/questions/63769746/3d-maze-solving-with-bfs-how-to-get-a-full-shortest-path-to-the-end-point-if-fo/63784659#63784659 – Matt Timmermans Apr 04 '21 at 14:26
  • so you propose a ' std::unordered_map ' for boolean? just saving the vertices that have been visited? – Hyung-Jo Kwon Apr 04 '21 at 14:34

1 Answers1

0

I think your code is fine but is failing to perform well as per the evaluators. That means you need to change algorithm. Have you tried other strategies like DFS?

public static void main(String[] args) throws IOException {
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter output = new BufferedWriter(new OutputStreamWriter(System.out));
    StringTokenizer st = new StringTokenizer(input.readLine());

    N = Integer.parseInt(st.nextToken());
    int dist = 1;
    map = new char[N][N];
    visited = new boolean[N][N];

    for (int i = 0; i < N; i++) {
        String line = input.readLine();
        for (int j = 0; j < N; j++) {
            char t = line.charAt(j);
            if (t == 'P') {
                sx = i;
                sy = j;
            }
            map[i][j] = line.charAt(j);
        }
    }

    long time = System.nanoTime();
    bfs(sx, sy, dist, "", ""); // bfs run
    time = System.nanoTime() - time;
    output.write(nswe + "  in:" + time + " with bfs\n");

    nswe = "";
    time = System.nanoTime();
    dfs(sx, sy, dist, "", new boolean[N][N]); // dfs run
    time = System.nanoTime() - time;
    output.write(nswe + " in:" + time + " with dfs");

    output.flush();
}

private static int shortest = Integer.MAX_VALUE;

public static void dfs(int x, int y, int dist, String dir, boolean[][] vstd) {
    int cur_x = x;
    int cur_y = y;
    vstd = Arrays.copyOf(vstd, vstd.length);
    for (int i = 0; i < vstd.length; i++) {
        vstd[i] = Arrays.copyOf(vstd[i], vstd[i].length);
    }
    String curnswe = dir;
    // System.out.println("\n"+dir);
    if (map[cur_x][cur_y] == 'G') {
        if (shortest >= dist) {
            shortest = dist;
            nswe = curnswe;
        }
        return;
    }
    dist++;
    if (shortest <= dist)
        return;
    for (int dir1 = 0; dir1 < 4; dir1++) {
        int r = cur_x + dr[dir1];
        int c = cur_y + dc[dir1];
        String d = compass[dir1];

        if (r >= 0 && c >= 0 && r < N && c < N) {
            if (map[r][c] == ' ' || map[r][c] == 'G')
                if (!vstd[r][c]) {
                    vstd[r][c] = true;
                    dfs(r, c, dist, dir + compass[dir1] + " ", vstd);
                }
        }
    }
}

Output:

8
########
  #    #
# # ## #
# #  #G#
#P   ###
#### # #
#G   #G#
########
E E E S S W W W  in:978000 with bfs
E E E S S W W W  in:188100 with dfs

For this input the dfs is finishing in very less time. Try with bigger examples.
Note: For huge inputs it might give StackOverflowError.

the Hutt
  • 16,980
  • 2
  • 14
  • 44
  • I have tried it with DFS too but I guess I will have to try it again. As time is alot faster. Weirdly it was slower for my DFS code. Could you elaborate on what you mean by evaluator? – Hyung-Jo Kwon Apr 04 '21 at 11:02
  • You'll find good info about Time limit exceeded error here [overcome-time-limit-exceedtle](https://www.geeksforgeeks.org/overcome-time-limit-exceedtle/) – the Hutt Apr 04 '21 at 11:14
  • In your code, in the main method, you ran bfs twice, instead of dfs – Hyung-Jo Kwon Apr 04 '21 at 12:27
  • :p Yup. I've corrected that. Also fixed dfs implementation. See the latest code – the Hutt Apr 04 '21 at 12:27
  • When I run it for just DFS, it produces nothing. I believe this is because when it searches a certain path and it comes up dead, it can't go back due to all the adjacent nodes have been visited. – Hyung-Jo Kwon Apr 04 '21 at 14:23
  • have you taken updated code? in latest code I've passed visited matrix to each recursion. And not maintained as global static field. – the Hutt Apr 04 '21 at 14:25
  • so you're creating a separate visited for every path dfs is taking? – Hyung-Jo Kwon Apr 04 '21 at 14:29
  • Yes. Each path need to maintain it's own visited matrix. Because if one path visited one point it doesn't mean other path shouldn't use it. Two paths shouldn't share their states. – the Hutt Apr 04 '21 at 14:32
  • 1
    I see, so that's the reason for Arrays.copyof() – Hyung-Jo Kwon Apr 04 '21 at 14:34
  • I submitted it to the online judge but it seems to be performing worse with DFS – Hyung-Jo Kwon Apr 04 '21 at 14:44
  • :( I wish we could get the use case they are running. Also, it depends on the order of directions you are taking. i.e. `compass = {"E","S","W","N"};` will be faster than `compass = {"N","W","S","E"};` for the use case you've mentioned here. :D – the Hutt Apr 04 '21 at 14:52
  • 1
    yes I agree, looking at the test data will make this process a lot easier. Ah but there's different tests that I have to run. Thanks for your help though! – Hyung-Jo Kwon Apr 04 '21 at 14:55