Begin with the End in the middle of everyday life is to determine the target, and then make plans . I learned this concept when I read management books before.
In the algorithm, starting with the end means that from the result until the initial state of the problem .
So when is the right time to think backwards? A very simple principle is:
- More positive thinking
- The code is difficult to write or the algorithm complexity is too high
At this time, we can consider the reverse operation.
My habit is that if it is difficult to solve directly, I will give priority to using ability detection dichotomy, if not, I will consider reverse thinking.
About the two points of the ability test, you can find it in my official account. You can reply to the two points in "Likoujiajia" to get it.
Today Sifa through three questions for everyone to talk about in the end how to use the time to write algorithms theme Begin with the End thought.
Robot jumping problem
This question comes from Niuke.com. Address: nowcoder.com/question/next?pid=16516564&qid=362295&tid=36905981
Topic description
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
机器人正在玩一个古老的基于DOS的游戏。游戏中有N+1座建筑——从0到N编号,从左到右排列。编号为0的建筑高度为0个单位,编号为i的建筑的高度为H(i)个单位。
起初, 机器人在编号为0的建筑处。每一步,它跳到下一个(右边)建筑。假设机器人在第k个建筑,且它现在的能量值是E, 下一步它将跳到第个k+1建筑。它将会得到或者失去正比于与H(k+1)与E之差的能量。如果 H(k+1) > E 那么机器人就失去 H(k+1) - E 的能量值,否则它将得到 E - H(k+1) 的能量值。
游戏目标是到达第个N建筑,在这个过程中,能量值不能为负数个单位。现在的问题是机器人以多少能量值开始游戏,才可以保证成功完成游戏?
输入描述:
第一行输入,表示一共有 N 组数据.
第二个是 N 个空格分隔的整数,H1, H2, H3, ..., Hn 代表建筑物的高度
输出描述:
输出一个单独的数表示完成游戏所需的最少单位的初始能量
输入例子1:
5
3 4 3 2 4
输出例子1:
4
输入例子2:
3
4 4 4
输出例子2:
4
输入例子3:
3
1 6 4
输出例子3:
3
ideas
The question asks how much energy is required at least initially. The forward solution will be more difficult, so my idea is:
- Ability test two points. For example, whether the energy x is OK, if x is OK, then the energy greater than x is OK. According to this, it is not difficult for us to write the code.
- Think backwards. Although we don't know what the initial energy is, we know that the final energy is 0 to be optimal, and we can also write code based on this.
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 due to:
next = pre + (pre - H[i])
therefore:
pre = (next + H[i]) / 2
ceil is required since division by 2 may result in decimals.
You can:
pre = math.ceil((next + H[i]) / 2)
You can also:
pre = (next + H[i] + 1) // 2
// is floor division, i.e. round 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 the minimum initial energy required, so we are not sure of the initial energy, we can be sure that the last building energy is 0 to be optimal.
2139. Minimum number of actions to get target value
The second question is the second question of a weekly competition in January 2022. The question is still relatively new.
topic address
https://leetcode-cn.com/problems/minimum-moves-to-reach-target-score/
Topic description
你正在玩一个整数游戏。从整数 1 开始,期望得到整数 target 。
在一次行动中,你可以做下述两种操作之一:
递增,将当前整数的值加 1(即, x = x + 1)。
加倍,使当前整数的值翻倍(即,x = 2 * x)。
在整个游戏过程中,你可以使用 递增 操作 任意 次数。但是只能使用 加倍 操作 至多 maxDoubles 次。
给你两个整数 target 和 maxDoubles ,返回从 1 开始得到 target 需要的最少行动次数。
示例 1:
输入:target = 5, maxDoubles = 0
输出:4
解释:一直递增 1 直到得到 target 。
示例 2:
输入:target = 19, maxDoubles = 2
输出:7
解释:最初,x = 1 。
递增 3 次,x = 4 。
加倍 1 次,x = 8 。
递增 1 次,x = 9 。
加倍 1 次,x = 18 。
递增 1 次,x = 19 。
示例 3:
输入:target = 10, maxDoubles = 4
输出:4
解释:
最初,x = 1 。
递增 1 次,x = 2 。
加倍 1 次,x = 4 。
递增 1 次,x = 5 。
加倍 1 次,x = 10 。
提示:
1 <= target <= 109
0 <= maxDoubles <= 100
ideas
Since the number is 1 at the beginning, the final state is target. So both forward thinking and reverse thinking are ok.
And if forward simulation, although it is easy to implement, but the time complexity is too high.
This is because starting at 1 we have two choices (if it can still be doubled), and there are still two choices next (if it can still be doubled). . .
So the time complexity is roughly $O(target * maxDoubles)$. The data range given by substituting the question is obviously not passed.
It is more difficult to think forward, we might as well think from the reverse.
From target, if target is odd, obviously we can only come by +1, even though we can still double. This reduces the time complexity. But that's not enough, and we find that if target is even, we should choose to double to target (if it can still be doubled), rather than +1 to target. This is because
- We are thinking backwards, if you don’t choose to double now but choose to double later, the benefits of doubling will be lower
- 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: Python3
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, then the time is about $log target$. And if the target is infinite, then the time is probably maxDoubles. So the time complexity is $O(min(maxDouble, log target))$
- Time complexity: $O(min(maxDouble, log target))$
- Space complexity: $O(1)$
LCP 20. Rapid Transit
The last question is the one of the force button cup, the difficulty is hard, let's take a look.
Title address (20. BRT)
https://leetcode-cn.com/problems/meChtZ/
Topic description
小扣打算去秋日市集,由于游客较多,小扣的移动速度受到了人流影响:
小扣从 x 号站点移动至 x + 1 号站点需要花费的时间为 inc;
小扣从 x 号站点移动至 x - 1 号站点需要花费的时间为 dec。
现有 m 辆公交车,编号为 0 到 m-1。小扣也可以通过搭乘编号为 i 的公交车,从 x 号站点移动至 jump[i]*x 号站点,耗时仅为 cost[i]。小扣可以搭乘任意编号的公交车且搭乘公交次数不限。
假定小扣起始站点记作 0,秋日市集站点记作 target,请返回小扣抵达秋日市集最少需要花费多少时间。由于数字较大,最终答案需要对 1000000007 (1e9 + 7) 取模。
注意:小扣可在移动过程中到达编号大于 target 的站点。
示例 1:
输入:target = 31, inc = 5, dec = 3, jump = [6], cost = [10]
输出:33
解释:
小扣步行至 1 号站点,花费时间为 5;
小扣从 1 号站台搭乘 0 号公交至 6 * 1 = 6 站台,花费时间为 10;
小扣从 6 号站台步行至 5 号站台,花费时间为 3;
小扣从 5 号站台搭乘 0 号公交至 6 * 5 = 30 站台,花费时间为 10;
小扣从 30 号站台步行至 31 号站台,花费时间为 5;
最终小扣花费总时间为 33。
示例 2:
输入:target = 612, inc = 4, dec = 5, jump = [3,6,8,11,5,10,4], cost = [4,7,6,3,7,6,4]
输出:26
解释:
小扣步行至 1 号站点,花费时间为 4;
小扣从 1 号站台搭乘 0 号公交至 3 * 1 = 3 站台,花费时间为 4;
小扣从 3 号站台搭乘 3 号公交至 11 * 3 = 33 站台,花费时间为 3;
小扣从 33 号站台步行至 34 站台,花费时间为 4;
小扣从 34 号站台搭乘 0 号公交至 3 * 34 = 102 站台,花费时间为 4;
小扣从 102 号站台搭乘 1 号公交至 6 * 102 = 612 站台,花费时间为 7;
最终小扣花费总时间为 26。
提示:
1 <= target <= 10^9
1 <= jump.length, cost.length <= 10
2 <= jump[i] <= 10^6
1 <= inc, dec, cost[i] <= 10^6
ideas
Since the starting point is 0, the ending point is target. As above, forward thinking and backward thinking are about the same difficulty.
So can we think positively? Like the above, there are too many cases of positive thinking, and the complexity is too high.
So how do you think backwards? How does reverse thinking optimize complexity?
Since the topic can reach the site with the number greater than the target during the moving process, we need to consider many points whose coordinates are greater than the target during the forward thinking process.
And if we think in reverse, we are that can't reach 161f3e8decd924 at a station with a number greater than 0 during the , so the situation is greatly reduced. And the station with the number greater than the target only needs to think about to the right and then takes the bus to return to the target (that is to say, we take the bus and then go back)
For each position pos, we consider:
- all walk
- Take the bus directly
- Take a few steps before taking the bus
Take the minimum value in these three cases.
The crux of 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 figure it out by pos % jump[i], and the point to start taking the bus is pos // jump.
code
- Language support: Python3
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
# 最坏的情况是全部走路,不乘公交,这种情况时间为 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)$
Summarize
Reverse thinking can often achieve the effect of dimensionality reduction. Sometimes it can make the solution idea simpler and the code easier to write. Sometimes it is possible to make fewer cases and less complexity.
Recall when to use reverse thinking? A very simple principle is:
- More positive thinking
- The code is difficult to write or the algorithm complexity is too high
I give you three examples of how to use the reverse thinking technique. in
- For 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 the second question, the situation of reverse thinking is greatly reduced, and the complexity is reduced exponentially, which is really a blow to dimensionality reduction.
- The third question uses the position that cannot exceed 0, and thinks backwards to reduce the complexity.
These issues still tip of the iceberg, do question the actual process you will find reverse thinking is very common, but there is no corresponding division algorithm mainstream topic Bale . I even have the idea to add it 91 Tian algorithm to learn , like the late plus enumeration chapters as , I think the reverse thinking is a thinking-based algorithm, gentlemen, please be sure to master!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。