SPOJ BITMAP超出时限

时间:2016-01-03 06:09:49

标签: java dynamic-programming breadth-first-search

问题(http://www.spoj.com/problems/BITMAP/)。我正在使用(https://medium.com/@arkro/spoj-bitmap-71d7a12354b1#.smnrwxcjv)上给出的优化方法。

以下是我的解决方案==>

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
class BITMAP {
    public static void print()
    {
        for(int i=0; i<r; i++)
        {
            System.out.println();
            for(int j=0; j<c; j++)
                System.out.print(a[i][j]+" ");
        }       
    }
    public static void bfs(int i1, int j1, int l)
    {
        a[i1][j1]=l;
        int p[] = {-1,0,1};
        Queue<Integer> qu = new LinkedList<Integer>();
        qu.add(i1);qu.add(j1);
        while(!qu.isEmpty())
        {       
            int i= qu.remove();
            int j= qu.remove();
            for(int m=0;m<3;m++)
            {
                for(int n=0;n<3;n++)
                {
                    int dist = Math.abs(p[m])+Math.abs(p[n]); 
                    if((i+p[m] >-1 && i+p[m]<r) && (j+p[n] >-1 && j+p[n]<c) && a[i+p[m]][j+p[n]]>a[i][j]+dist)
                    {
                        a[i+p[m]][j+p[n]] = a[i][j]+dist;
                        qu.add(i+p[m]);qu.add(j+p[n]);                      
                    }                   
                }           
            }       
        }
    }
    public static int a[][]= new int[182][182];
    public static int r;
    public static int c;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int t = in.nextInt();
        while(t--!=0)
        {
            r = in.nextInt();
            c = in.nextInt();
            ArrayList<int[]> l = new ArrayList<int[]>(); //Using it for storing all 1's location in the entire grid.

            for(int i=0; i<r; i++)
            {
                String str = in.next();
                for(int j=0; j<c; j++)
                {                  
                    if(str.charAt(j)=='1')
                        l.add(new int[]{i,j});
                    a[i][j]=Integer.MAX_VALUE;
                }               
            }

            for(int[] k:l)
                bfs(k[0],k[1],0);   
            print();
            in.nextLine();
        }
        in.close();
    }
}

我应该怎样做才能消除这个TLE问题?

1 个答案:

答案 0 :(得分:0)

问题 1

链接文章中描述的方法效率低下。它从棋盘上标有 1 的每个顶点开始 BFS 遍历。遍历以较少的步骤探索前一个 BFS 尚未到达的每个板顶点。这可能会导致多次探索某些顶点。

考虑以下示例:

1000000000000000000000
0000000000000000000001

有两个顶点标有 1。第一个 BFS 将从左上角的 1 开始。它将探索整个棋盘,记录每个顶点到起始顶点的距离。第二个 BFS 将从右下角的 1 开始。它将探索棋盘的一半,直到到达离左上角 1 比离右下角 1 更近的顶点。因此,一半的棋盘已经被遍历了两次。

棋盘上的 1 越多,这个问题就越严重。以这个板子为例:

1111111111111111111111
0000000000000000000000
0000000000000000000000
0000000000000000000000
0000000000000000000000

第一个 BFS 将遍历整个板。第二个 BFS 必须探索除第一列之外的整个板(板上唯一比第二个 1 更靠近第一个 1 的地方)。第三个 BFS 再次探索除了前两列之外的整个板,依此类推。

此问题有一个简单的解决方法:您可以从所有 1 顶点同时开始,而不是为每个 1 执行单独的 BFS。这可以通过使用 1 顶点初始化 BFS 队列来完成。

然后 BFS 照常继续:它将遍历位于队列开头的 1 顶点,并将其未访问的邻居添加到队列末尾。因此,在从队列中移除所有 1 顶点后,BFS 将探索与任何 1 顶点距离为 1 的顶点。之后,它将探索与任何 1 顶点距离为 2 的顶点,依此类推。

这样,只需探索一次棋盘,而不是多次探索棋盘的某些部分。

问题 2

除了超时问题,在你的实现中还有一个正确性问题:它对棋盘进行了 8 向遍历,而不是 4 向遍历(意味着你直线和对角走,而不是只直线).

问题描述中使用的度量是 Manhattan metric,它通过将两点在每个轴上的距离相加来测量两点之间的距离。因此,两个水平或垂直相邻的点的距离为 1,但两个对角相邻的点的距离为 2。因此,当您让 BFS 对角移动时,您错误地将对角相邻点之间的距离测量为 1 而不是 2。

要解决此问题,请限制您的 BFS 采取对角线步骤。