LeetCode 858 - Mirror Reflection


https://leetcode.com/problems/mirror-reflection/description/
There is a special square room with mirrors on each of the four walls.  Except for the southwest corner, there are receptors on each of the remaining corners, numbered 01, and 2.
The square room has walls of length p, and a laser ray from the southwest corner first meets the east wall at a distance q from the 0th receptor.
Return the number of the receptor that the ray meets first.  (It is guaranteed that the ray will meet a receptor eventually.)

Example 1:
Input: p = 2, q = 1
Output: 2
Explanation: The ray meets receptor 2 the first time it gets reflected back to the left wall.
https://leetcode.com/problems/mirror-reflection/discuss/141790/The-Most-Straight-Forward-Solution.Pure-Math.Only-13ms
First, think about the case p = 3 & q = 2.
image
So, this problem can be transformed into finding m * p = n * q, where
m = the number of room extension + 1.
n = the number of light reflection + 1.
  1. If the number of light reflection is odd (which means n is even), it means the corner is on the left-hand side. The possible corner is 2.
    Otherwise, the corner is on the right-hand side. The possible corners are 0 and 1.
  2. Given the corner is on the right-hand side.
    If the number of room extension is even (which means m is odd), it means the corner is 1. Otherwise, the corner is 0.
So, we can conclude:
m is even & n is odd => return 0.
m is odd & n is odd => return 1.
m is odd & n is even => return 2.
Note: The case m is even & n is even is impossible. Because in the equation m * q = n * p, if m and n are even, we can divide both m and n by 2. Then, m or n must be odd.


--
Because we want to find m * p = n * q, where either m or n is odd, we can do it this way.
https://leetcode.com/problems/mirror-reflection/discuss/141773/C%2B%2BJavaPython-1-line-without-using-any-package-or
Here is my first solution. When I did it, I just code without any thinking.
    def mirrorReflection(self, p, q):
        k = 1
        while (q * k % p): k += 1
        if q * k / p % 2 and k % 2: return 1
        if q * k / p % 2 == 0 and k % 2: return 0
        if q * k / p % 2 and k % 2 == 0: return 2
        return -1
When I reviewed the problems for my discuss article, I finaly realised only odd or even matter.
Divide p,q by 2 until at least one odd.
If p = odd, q = even: return 0
If p = even, q = odd: return 2
If p = odd, q = odd: return 1
I summary it as return 1 - p % 2 + q % 2

    public int mirrorReflection(int p, int q) {
        while (p % 2 == 0 && q % 2 == 0) {p >>= 1; q >>= 1;}
        return 1 - p % 2 + q % 2;
    }

Instead of modelling the ray as a bouncing line, model it as a straight line through reflections of the room.
For example, if p = 2, q = 1, then we can reflect the room horizontally, and draw a straight line from (0, 0) to (4, 2). The ray meets the receptor 2, which was reflected from (0, 2) to (4, 2).
In general, the ray goes to the first integer point (kp, kq) where k is an integer, and kp and kq are multiples of p. Thus, the goal is just to find the smallest k for which kq is a multiple of p.
The mathematical answer is k = p / gcd(p, q).
Time Complexity: O(\log P), the complexity of the gcd operation.
  public int mirrorReflection(int p, int q) {
    int g = gcd(p, q);
    p /= g;
    p %= 2;
    q /= g;
    q %= 2;

    if (p == 1 && q == 1)
      return 1;
    return p == 1 ? 0 : 2;
  }

  public int gcd(int a, int b) {
    if (a == 0)
      return b;
    return gcd(b % a, a);

  }

Approach 1: Simulation
The initial ray can be described as going from an origin (x, y) = (0, 0) in the direction (rx, ry) = (p, q). From this, we can figure out which wall it will meet and where, and what the appropriate new ray will be (based on reflection.) We keep simulating the ray until it finds it's destination.
Algorithm
The parameterized position of the laser after time t will be (x + rx * t, y + ry * t). From there, we know when it will meet the east wall (if x + rx * t == p), and so on. For a positive (and nonnegligible) time t, it meets the next wall.
We can then calculate how the ray reflects. If it hits an east or west wall, then rx *= -1, else ry *= -1.
In Java, care must be taken with floating point operations.
Time Complexity: O(p). We can prove (using Approach #2) that the number of bounces is bounded by this.

  double EPS = 1e-6;

  public int mirrorReflection(int p, int q) {
    double x = 0, y = 0;
    double rx = p, ry = q;

    // While it hasn't reached a receptor,...
    while (!(close(x, p) && (close(y, 0) || close(y, p)) || close(x, 0) && close(y, p))) {
      // Want smallest t so that some x + rx, y + ry is 0 or p
      // x + rxt = 0, then t = -x/rx etc.
      double t = 1e9;
      if ((-x / rx) > EPS)
        t = Math.min(t, -x / rx);
      if ((-y / ry) > EPS)
        t = Math.min(t, -y / ry);
      if (((p - x) / rx) > EPS)
        t = Math.min(t, (p - x) / rx);
      if (((p - y) / ry) > EPS)
        t = Math.min(t, (p - y) / ry);

      x += rx * t;
      y += ry * t;

      if (close(x, p) || close(x, 0))
        rx *= -1;
      if (close(y, p) || close(y, 0))
        ry *= -1;
    }

    if (close(x, p) && close(y, p))
      return 1;
    return close(x, p) ? 0 : 2;
  }

  public boolean close(double x, double y) {
    return Math.abs(x - y) < EPS;
  }


https://www.acwing.com/solution/LeetCode/content/810/
有一个特殊的正方形房间,每面墙上都有一面镜子。除西南角以外,每个角落都放有一个接受器,编号为 01,以及 2
正方形房间的墙壁长度为 p,一束激光从西南角射出,首先会与东墙相遇,入射点到接收器 0 的距离为 q
返回光线最先遇到的接收器的编号(保证光线最终会遇到一个接收器)。

样例

输入:p = 2, q = 1
输出:2
解释:这条光线在第一次被反射回左边的墙时就遇到了接收器 2。

不管光线在里面怎么折射,光线在水平方向走p,那么在垂直方向必定走q. 找最小公倍数。
(找规律) O(logp+logq)
  1. 求出 p 和 q 的最小公倍数 lcm,令 x = lcm / py = lcm / q
  2. 如果 y 为奇数,则说明最终会被右墙的接收器接收,此时若 x 也为奇数,则说明是右上角,否则是右下角。
  3. 如果 y 为偶数,如果题目保证了一定能被接收,则一定是左上角的接收器。

时间复杂度

  • 算法核心在求最大公约数,时间复杂度为 O(logp+logq)

空间复杂度

  • 由于使用了递归辗转相处,故需要额外的 O(logp+logq) 的栈空间。
这道题给了我们一个正方形的房间,说是四面都是镜子墙,然后在西南角有一个激光发射器,其余三个角都有接收装置,问我们最终激光会被哪个接收器接收。第一次读题时这句 "Return the number of the receptor that the ray meets first." 让博主理解错误了,以为是让返回接收器的个数,以为接收器也能反射激光到其对角的接收器,那么接收器2和0互相反射,就是返回经过了2个接收器,接收器1返回到反射点,就是返回经过了1个接收点,想的是一套一套的,结果人家让返回的是接收器的标号,个人觉得将 number 改为 index 会减少些歧义。无所谓了,反正最终搞懂了题意就行了。其实这道题的正确解法还挺难想的,因为大家很容易走进的误区就是研究反射角啥的,然后算具体反射到了哪一个位置,再算下一个位置,其实这样都将题目变复杂了。博主把这一类型归为脑筋急转弯 Brain Teaser,一般都有很巧妙的数学解法,并不需要太复杂的算法。
首先从最简单的情况开始分析,当p和q相等的时候,那么激光直接到达接收器1,当 p/q = 2 的时候,就如例子中所示,经过右边的镜面反射后到达左上角的接受器2。那么我们再来考虑下这三种情况 p/q = 3, p/q = 4, p/q = 3/2,并画出折射情况如下所示:
这里就有些比较好玩的规律了,我们知道激光遇到镜面是会发生折射的,但是假如没有镜面,就会仍然沿直线前进,那么对于 p/q = 3 时,若我们在右边增加大小相同的2个房间,则激光会到达右上角,由于第二个房间和原始房间是镜面对称的,而第三个房间和第二个房间也是镜面对称的,则第三个房间和原始房间就是一样的了,那么就可以假设一下,奇数房间和原始房间的布局相同。再来看上图中的 p/q = 4 时,我们在右边复制了三个房间,在第四个房间的时候,激光到达了右上角,而第偶数个房间的布局是跟原始房间称镜面反射的,则就是接受器2了。其实有些时候,我们不止要在右边复制房间,还需要在上面复制房间,比如当 p/q = 3/2 时,我们需要复制出一个 2x3 大小的矩阵出来,在水平方向共有三个房间,是奇数则水平方向和原始房间布局一致,但是竖直方向也复制了房间,那么竖直方向有偶数个房间,则竖直方向和原始房间成镜面反射,则最右上角为接收器0。
分析到这里,我们应该已经能总结出规律如下了:
  • p为奇数,q为奇数时,到达接收器1。
  • p为奇数,q为偶数时,到达接收器0。
  • p为偶数,q为奇数时,到达接收器2。
那你可能会有疑问了,为啥没有p和q均为偶数的情况呢?比如 p = 4, q = 2,其实只要我们画个图就知道,这个跟 p = 2, q = 1 的情况是一摸一样的,若p和q均为偶数,那么那么一定可以同时除以2,那么其实我们可以先对p和q进行判断,若二者同为偶数,则同时除以2,直到不同时为偶数时,然后再带入上面归纳的三种情况求解即可,参见代码如下:


解法一:
class Solution {
public:
    int mirrorReflection(int p, int q) {
        while (p % 2 == 0 && q % 2 == 0) {
            p /= 2; q /= 2;
        }
        if (p % 2 == 0) return 2;
        if (q % 2 == 0) return 0;
        return 1;
    }
};


其实我们可以进一步化简,将三种情况融为一个表达式即可,即 1 - p%2 + q%2,不信的话可以带数字验证一下,碉堡了有木有,参见代码如下:


解法二:
class Solution {
public:
    int mirrorReflection(int p, int q) {
        while (p % 2 == 0 && q % 2 == 0) {
            p /= 2; q /= 2;
        }
        return 1 - p % 2 + q % 2;
    }
};


其实不光是p和q同时为偶数的时候可以化简,只要p和q的最大公约数 Greatest Common Divisor 大于1时,都可以化简,比如,若 p = 6, q = 3 时跟 p = 2, q = 1 的情况也是一样,那我们就可以先求出p和q的最大公约数,然后用p和q分别除以这个最大公约数,再带入上面的那个一行公式求解即可,参见代码如下:


解法三:
class Solution {
public:
    int mirrorReflection(int p, int q) {
        return 1 - p / gcd(p, q) % 2 + q / gcd(p, q) % 2;
    }
    int gcd(int p, int q) {
        return q ? gcd(q, p % q) : p;
    }
};
X. Video
leetcode 858. Mirror Reflection 算法讲解

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts