[Daily Coding Problem 68] Count Pairs of attacking bishop pairs

This problem was asked by Google.

On our special chessboard, two bishops attack each other if they share the same diagonal. This includes bishops that have another bishop located between them, i.e. bishops can attack through pieces.

You are given N bishops, represented as (row, column) tuples on a M by M chessboard. Write a function to count the number of pairs of bishops that attack each other. The ordering of the pair doesn't matter: (1, 2) is considered the same as (2, 1).

For example, given M = 5 and the list of bishops:

  • (0, 0)
  • (1, 2)
  • (2, 2)
  • (4, 0)

The board would look like this:

[b 0 0 0 0]
[0 0 b 0 0]
[0 0 b 0 0]
[0 0 0 0 0]
[b 0 0 0 0]

You should return 2, since bishops 1 and 3 attack each other, as well as bishops 3 and 4.

The naive solution is to check each pair of all possible pairs. Its runtime is O(N^2). Can we make this any faster?

The bottle neck of the naive solution is that on a particular diagonal, if there are m bishops, then it takes m * (m - 1) / 2, which is O(m^2) time. It does not use the information that all of these bishops are in fact on the same diagonal already. A better solution is to go through each bishop and bucket them into each separate diagonal. For each diagonal with m bishops on it, there are m * (m - 1) / 2 attacking pairs. This reduces the runtime of getting all attacking pairs from one diagonal to O(m). 

Now we just need to find a good way of bucketing all bishops. We need a slope and a point to definitively specify a line in a 2D-coordinates. In this case, there can be only 2 slopes, +1 or -1.  And we can use the boundary coordinates of the chessboard to bucket each bishops.

For bishop (x, y),

1. when the slope is 1, if x > y, the boundary point is (x - y, y - y); else the boundary point is (x - x, y - x); so boundary point is (x - min(x, y), y - min(x, y)).

2. when the slope is -1, if x > y, the boundary point is (x - x, y + x); else the boundary point is (x - (M - y), y + (M -y)); so the boundary point is (x - min(x, M - y), y + min(x, M - y)).

We can use two maps of map save the diagonals buckets. We must use 2 separate maps, 1 for each slope because for each boundary point, there are 2 possilbe diagonals, one with slope 1, one with slope -1.

 1 public class AttackingBishop {
 2     class Point {
 3         int x, y;
 4     }
 5     public int countAttackingBishopPairs(Point[] points, int M) {
 6         Map<Integer, Map<Integer, Integer>> posMap = new HashMap<>();
 7         Map<Integer, Map<Integer, Integer>> negMap = new HashMap<>();
 8         for(Point point : points) {
 9             int posBoundaryX = point.x - Math.min(point.x, point.y);
10             int posBoundaryY = point.y - Math.min(point.x, point.y);
11             int negBoundaryX = point.x - Math.min(point.x, M - point.y);
12             int negBoundaryY = point.y + Math.min(point.x, M - point.y);
13 
14             if(!posMap.containsKey(posBoundaryX)) {
15                 posMap.put(posBoundaryX, new HashMap<>());
16             }
17             if(!posMap.get(posBoundaryX).containsKey(posBoundaryY)) {
18                 posMap.get(posBoundaryX).put(posBoundaryY, 0);
19             }
20             Map<Integer, Integer> map = posMap.get(posBoundaryX);
21             map.put(posBoundaryY, map.get(posBoundaryY) + 1);
22 
23             if(!negMap.containsKey(negBoundaryX)) {
24                 negMap.put(negBoundaryX, new HashMap<>());
25             }
26             if(!negMap.get(negBoundaryX).containsKey(negBoundaryY)) {
27                 negMap.get(negBoundaryX).put(negBoundaryY, 0);
28             }
29             map = negMap.get(negBoundaryX);
30             map.put(negBoundaryY, map.get(negBoundaryY) + 1);
31         }
32         int count = 0;
33         for(Map<Integer, Integer> map : posMap.values()) {
34             for(int c : map.values()) {
35                 count += c * (c - 1) / 2;
36             }
37         }
38         for(Map<Integer, Integer> map : negMap.values()) {
39             for(int c : map.values()) {
40                 count += c * (c - 1) / 2;
41             }
42         }
43         return count;
44     }
45 }

A more concise implementation is to customize your own key and only use a hash map.

 1 public class AttackingBishop {
 2     class Point {
 3         int x, y;
 4 
 5         Point(int x, int y) {
 6             this.x = x;
 7             this.y = y;
 8         }
 9 
10         @Override
11         public int hashCode() {
12             return 31 * x + 37 * y;
13         }
14 
15         @Override
16         public boolean equals(Object obj) {
17             if (this == obj) {
18                 return true;
19             }
20             if (obj == null || getClass() != obj.getClass()) {
21                 return false;
22             }
23             Point p = (Point) obj;
24             if (x != p.x || y != p.y) {
25                 return false;
26             }
27             return true;
28         }
29     }
30 
31     public int countAttackingBishopPairs(Point[] points, int M) {
32         Map<Point, Integer> posMap = new HashMap<>();
33         Map<Point, Integer> negMap = new HashMap<>();
34         for (Point point : points) {
35             Point p1 = new Point(point.x - Math.min(point.x, point.y), point.y - Math.min(point.x, point.y));
36             Point p2 = new Point(point.x - Math.min(point.x, M - point.y), point.y + Math.min(point.x, M - point.y));
37             if (!posMap.containsKey(p1)) {
38                 posMap.put(p1, 1);
39             } else {
40                 posMap.put(p1, posMap.get(p1) + 1);
41             }
42             if (!negMap.containsKey(p2)) {
43                 negMap.put(p2, 1);
44             } else {
45                 negMap.put(p2, negMap.get(p2) + 1);
46             }
47         }
48         int count = 0;
49         for (int c : posMap.values()) {
50             count += c * (c - 1) / 2;
51         }
52         for (int c : negMap.values()) {
53             count += c * (c - 1) / 2;
54         }
55         return count;
56     }
57 }
原文地址:https://www.cnblogs.com/lz87/p/10351395.html