Heard that reverse thinking can reduce time complexity?

Posted by sitestem on Fri, 28 Jan 2022 19:22:38 +0100

Starting with the end means setting goals first and then making plans in daily life. I learned this concept when I read management books before.

In the algorithm, taking the end as the beginning refers to pushing back from the result to the initial state of the problem.

So when is it appropriate to think in reverse? A very simple principle is:

  1. There are many cases of positive thinking
  2. The code is difficult to write or the algorithm complexity is too high

At this time, we can consider reverse operation.

My habit is that if it is difficult to solve directly, I will give priority to using ability test dichotomy. If not, I will consider reverse thinking.

About the two points of ability test, you can find it in my official account, and you can get two points in "KL Kaga".

Today, western France will talk to you about how to use the idea of beginning with the end when writing algorithm problems through three questions.

Robot jumping problem

This question comes from niuke.com. Address: nowcoder com/question/next? pid=16516564&qid=362295&tid=36905981

Title Description

time limit: C/C++ 1 Seconds, other languages 2 seconds

Space limitations: C/C++ 32M,Other languages 64 M

Robots are playing an ancient game based on DOS Game. In the game N+1 Buildings - from 0 to N Number, arranged from left to right. The building with No. 0 has a height of 0 units and No i The height of the building is H(i)Units.

At first, the robot was at the building numbered 0. With each step, it jumps to the next (right) building. Suppose the robot is in the second stage k A building, and its current energy value is E, Next, it will jump to the k+1 Architecture. It will get or lose proportional to H(k+1)And E The difference in energy. If H(k+1) > E Then the robot will lose H(k+1) - E Energy value, otherwise it will get E - H(k+1) Energy value of.

The goal of the game is to reach the N Building, in this process, the energy value cannot be negative units. The question now is, how much energy does the robot start the game to ensure the successful completion of the game?

Enter description:
The first line of input indicates a total of N Group data.

The second is N A space delimited integer, H1, H2, H3, ..., Hn Represents the height of the building

Output description:
Output a single number that represents the minimum unit of initial energy required to complete the game

Input example 1:
5
3 4 3 2 4

Output example 1:
4

Input example 2:
3
4 4 4

Output example 2:
4

Input example 3:
3
1 6 4

Output example 3:
3

thinking

The question requires at least how much energy is needed in the initial situation. Forward solution will be difficult, so my idea is:

  1. Ability test: 2 points. For example, whether the energy X can, if x can, then the energy greater than x can. So it's not difficult for us to write code.
  2. Reverse thinking. Although we don't know the initial energy, we know that the final energy is 0. Based on this, we can also write code.

Here we use the second method.

Specifically: we think from the back to the front. The energy to reach the last level is at least 0. And because:

next = pre + (pre - H[i])

Therefore:

pre = (next + H[i]) / 2

Because dividing by 2 may result in decimals, ceil is required.

You can:

pre = math.ceil((next + H[i]) / 2)

it's fine too:

pre = (next + H[i] + 1) // 2

//Is the division of the floor, i.e. rounding down

Code (Python)

n = input()
H = input().split(" ")
ans = 0
for i in range(len(H) - 1, -1, -1):
    ans = (ans + int(H[i]) + 1) // 2
print(ans)

Complexity analysis

  • Time complexity: $O(n)$
  • Space complexity: $O(1)$

The key sentence summary of this topic is: we need to determine how much initial energy we need at least. Therefore, we are not sure about the initial energy. What we can determine is that it is optimal when the last building energy is 0.

2139. Minimum number of actions to get the target value

The second question is the second question of a weekly match in January 2022. The question is still relatively new.

Title address

https://leetcode-cn.com/probl...

Title Description

You are playing a round number game. Start with integer 1 and expect to get an integer target . 

In one action, you can do one of the following two operations:

Increment, increasing the value of the current integer by 1 (that is, x = x + 1). 
Double to double the value of the current integer (that is, x = 2 * x). 

Throughout the game, you can use incremental operations any number of times. However, you can only use the double operation maxDoubles Times.

Here are two integers target and maxDoubles ,Return from 1 target The minimum number of actions required.

 

Example 1:

Input: target = 5, maxDoubles = 0
 Output: 4
 Explanation: increment by 1 until you get target . 


Example 2:

Input: target = 19, maxDoubles = 2
 Output: 7
 Explanation: initially, x = 1 . 
Increment 3 times, x = 4 . 
Double once, x = 8 . 
Increment once, x = 9 . 
Double once, x = 18 . 
Increment once, x = 19 . 


Example 3:

Input: target = 10, maxDoubles = 4
 Output: 4
 Explanation:
first, x = 1 . 
Increment once, x = 2 . 
Double once, x = 4 . 
Increment once, x = 5 . 
Double once, x = 10 . 


 

Tips:

1 <= target <= 109
0 <= maxDoubles <= 100

thinking

Since the initial number is 1, the final status is target. Therefore, both forward thinking and reverse thinking are ok.

If forward simulation is used, it is easy to implement, but the time complexity is too high.

This is because we have two choices from 1 (if we can still double), and then we still have two choices (if we can still double)...

Therefore, the time complexity is roughly $O(target * maxDoubles) $. It is obvious that the data range given by the topic cannot be passed.

It is difficult to think in the positive direction. We might as well think in the reverse direction.

Starting from target, if target is odd, obviously we can only come through + 1, even if we can still double. In this way, the time complexity is reduced. However, this is not enough. Further, we found that if the target is even, we should choose to double to target (if we can still double), rather than + 1 to target. that is because

  1. We are thinking in the opposite direction. If you do not choose to double now, but choose to double later, the benefit brought by doubling will be lower
  2. The benefit of doubling must be greater than + 1. In other words, doubling can reach the target faster.

Based on this, it is not difficult to write the following code.

code

  • Language support: Python 3

Python3 Code:

class Solution:
    def minMoves(self, target: int, maxDoubles: int) -> int:
        ans = 0
        while maxDoubles and target != 1:
            ans += 1
            if target % 2 == 1:
                target -= 1
            else:
                maxDoubles -= 1
                target //= 2
        ans += (target - 1)
        return ans

Complexity analysis

If maxDoubles is infinite, the time is probably $log target $. If the target is infinite, the time is probably maxDoubles. Therefore, the time complexity is $O(min(maxDouble, log target))$

  • Time complexity: $O(min(maxDouble, log target))$
  • Space complexity: $O(1)$

LCP 20. Bus Rapid Transit

The last question is the one about Li Kou cup. The difficulty is hard. Let's have a look.

Title address (20. BRT)

https://leetcode-cn.com/probl...

Title Description

Xiaokou plans to go to the autumn market. Due to the large number of tourists, Xiaokou's moving speed is affected by the flow of people:

Small buckle from x Move site No. to x + 1 The time required for site No inc;
Small buckle from x Move site No. to x - 1 The time required for site No dec. 

existing m Buses numbered 0 to m-1. The small buckle can also be marked with i Bus, from x Move site No. to jump[i]*x No. site, time-consuming only cost[i]. The small buckle can take any number of buses, and the number of buses is unlimited.

It is assumed that the starting site of small deduction is recorded as 0, and the site of autumn market is recorded as 0 target,Please return to Xiaokou. How long does it take to reach the autumn market at least. Due to the large number, the final answer needs to be 1000000007 (1e9 + 7) Take the mold.

Note: the small buckle can arrive during movement, and the number is greater than target Your site.

Example 1:

Input: target = 31, inc = 5, dec = 3, jump = [6], cost = [10]

Output: 33

Explanation:
It takes 5 minutes to walk to station 1;
Take bus No. 0 from platform 1 to platform 6 * 1 = 6 Platform, time spent is 10;
It takes 3 hours for Xiaokou to walk from platform 6 to platform 5;
Xiaokou takes bus No. 0 from platform 5 to platform 6 * 5 = 30 Platform, time spent is 10;
It takes 5 minutes for Xiaokou to walk from platform 30 to platform 31;
The total time spent on the final deduction is 33.

Example 2:

Input: target = 612, inc = 4, dec = 5, jump = [3,6,8,11,5,10,4], cost = [4,7,6,3,7,6,4]

Output: 26

Explanation:
It takes 4 hours for Xiaokou to walk to station 1;
Take bus No. 0 from platform 1 to platform 3 * 1 = 3 Platform, time spent is 4;
Xiaokou takes bus No. 3 from platform 3 to 11 * 3 = 33 Platform, time spent is 3;
It takes 4 hours for Xiaokou to walk from platform 33 to platform 34;
Xiaokou takes bus No. 0 from platform 34 to 3 * 34 = 102 Platform, time spent is 4;
Xiaokou takes bus No. 1 from platform 102 to 6 * 102 = 612 Platform, time spent is 7;
The total time spent on the final deduction is 26.

Tips:

1 <= target <= 10^9
1 <= jump.length, cost.length <= 10
2 <= jump[i] <= 10^6
1 <= inc, dec, cost[i] <= 10^6

thinking

Since the starting point is 0 and the ending point is target. As above, forward thinking and reverse thinking are almost as difficult.

So can we think positively? As above, there are too many positive thinking situations and the complexity is too high.

So how to think in reverse? How to optimize complexity by reverse thinking?

Since the topic can reach the site with the number greater than target in the process of moving, we need to consider many points with the coordinates greater than target in the process of forward thinking.

If we think in reverse, we can't reach the site with the number greater than 0 in the process of moving, so the situation is greatly reduced. For stations with a number greater than target, we only need to think about moving to the right and then taking the bus back to target (that is, we take the bus and then go back)

For each position pos, we think:

  1. All walk
  2. Take the bus directly
  3. Take a few steps before you take the bus

In these three cases, the minimum value can be taken.

The key to the problem is case 3. How do we calculate how many steps to take before taking the bus? If we think in reverse, we can easily calculate it through POS% jump [i], and the point where we start to take the bus is pos // jump.

code

  • Language support: Python 3

Python3 Code:

class Solution:
    def busRapidTransit(self, target: int, inc: int, dec: int, jumps: List[int], cost: List[int]) -> int:
        @lru_cache(None)
        def dfs(pos):
            if pos == 0: return 0
            if pos == 1: return inc
            # The worst case is to walk all the way without taking the bus. In this case, the time is pos * inc
            ans = pos * inc
            for i, jump in enumerate(jumps):
                pre_pos, left = pos // jump, pos % jump
                if left == 0: ans = min(ans, cost[i] + dfs(pre_pos))
                else: ans = min(ans, cost[i] + dfs(pre_pos) + inc * left, cost[i] + dfs(pre_pos + 1) + dec * (jump - left))
            return ans
        return dfs(target) % 1000000007

Complexity analysis

Let T be the length of the jump array.

  • Time complexity: $O(target * T)$
  • Space complexity: $O(target)$

summary

Reverse thinking can often achieve the effect of dimensionality reduction. Sometimes it can make the solution easier and the code easier to write. Sometimes it can make the situation less and the complexity less.

Review when to use reverse thinking? A very simple principle is:

  1. There are many cases of positive thinking
  2. The code is difficult to write or the algorithm complexity is too high

I give you three examples to illustrate how to use the reverse thinking technique. among

  • In the first question, forward thinking can only be enumerated one by one. Of course, we can use dichotomy to reduce the complexity, but the complexity is still less than reverse thinking.
  • In question 2, the reverse thinking situation is greatly reduced, and the complexity index is reduced. It's really a blow to dimensionality reduction.
  • The third question uses the position of no more than 0 to reverse thinking and reduce complexity.

These questions are still the tip of the iceberg. In the actual problem-solving process, you will find that reverse thinking is very common, but the mainstream algorithm Division has no corresponding topic. I even have the idea to add it to the 91 day learning algorithm. Just like the later enumeration chapter, I think reverse thinking is also a basic algorithm thinking. Please be sure to master it!

Topics: Algorithm leetcode