题目地址:
https://leetcode.com/problems/minimum-initial-energy-to-finish-tasks/
给定若干任务 A A A,每个任务有两个性质,一个是其消耗的能量,另一个是开始做它的时候必须具备的初始能量(门槛能量)。这些任务可以按任何顺序一个接着一个做。问初始至少要多少能量才能将所有任务都完成。题目保证门槛能量大于等于消耗能量。
思路是贪心。我们逆过来想, 如果我们初始有 0 0 0能量,然后经过一个个任务,回到初始状态,会是什么样的情况。我们考虑正在做任务 t t t,其消耗的能量是 t x t_x tx,门槛是 t y t_y ty,假设做完这个任务之后的能量是 z z z,那么”回滚“任务之后的能量就是 z + t x z+t_x z+tx,但是,回滚之后的能量还必须至少是 t y t_y ty,所以完成该任务之后的能量应当满足 z + t x ≥ t y z+t_x\ge t_y z+tx≥ty,即 z ≥ t y − t x z\ge t_y-t_x z≥ty−tx,所以做该任务之前的能量应该至少是 max { t x + z , t y } \max\{t_x+z,t_y\} max{tx+z,ty}。由于 t y ≥ t x t_y\ge t_x ty≥tx,所以我们不但要把消耗的能量 t x t_x tx补回来,还有可能需要额外补 t y − t x − z t_y-t_x-z ty−tx−z。那么我们将所有任务按照门槛能量减去消耗能量(即可能要额外补充的能量)进行从小到大排序,然后依次回滚任务,这样就能达到额外补足的能量都尽量仅仅用于达到门槛,而不会有多余的浪费。我们猜测,按这样的顺序回滚所得的初始能量应该是最少的。
算法正确性证明:
设
A
A
A已经按照差从小到大排序,然后考虑回滚任务。如果初始能量是
w
w
w,然后我们考虑回滚两个任务
(
a
,
a
+
u
)
(a,a+u)
(a,a+u)和
(
b
,
b
+
v
)
(b,b+v)
(b,b+v),其中
u
<
v
u<v
u<v。如果先回滚任务
1
1
1,得到的必须的最少能量是
max
{
a
+
w
,
a
+
u
}
\max\{a+w,a+u\}
max{a+w,a+u},再回滚任务
2
2
2,得
max
{
max
{
a
+
w
,
a
+
u
}
+
b
,
b
+
v
}
=
max
{
max
{
w
,
u
}
+
a
+
b
,
b
+
v
}
\max\{\max\{a+w,a+u\}+b,b+v\}=\max\{\max\{w,u\}+a+b,b+v\}
max{max{a+w,a+u}+b,b+v}=max{max{w,u}+a+b,b+v};如果先回滚任务
2
2
2再任务
1
1
1,则得
max
{
max
{
w
,
v
}
+
a
+
b
,
b
+
u
}
\max\{\max\{w,v\}+a+b,b+u\}
max{max{w,v}+a+b,b+u},由于
max
{
max
{
w
,
u
}
+
a
+
b
,
b
+
v
}
<
max
{
max
{
w
,
v
}
+
a
+
b
,
b
+
u
}
\max\{\max\{w,u\}+a+b,b+v\}<\max\{\max\{w,v\}+a+b,b+u\}
max{max{w,u}+a+b,b+v}<max{max{w,v}+a+b,b+u}所以先回滚任务
1
1
1能得到更少的能量。所以按照门槛能量减去消耗能量从小到大排列来回滚,最后能得到最少的能量。所以对于任务数小于等于
2
2
2的时候,算法正确。接下来数学归纳法。假设任务数小于
n
n
n的时候都是对的,当任务数等于
n
n
n的时候,如果最优解里任务
A
[
0
]
A[0]
A[0]不是第一个回滚,那么我们可以将
A
[
0
]
A[0]
A[0]向前对换若干次换到首先回滚,由此所得的解一定也是最优解(因为每次对换的时候都得到了更优解,但是当前解已经最优了,所以每次对换得到的还是最优解)。接着由归纳假设,后面的
n
−
1
n-1
n−1个任务按照差从小到大回滚也是最优的。再由数学归纳法,所以算法正确。
代码如下:
import java.util.Arrays;
public class Solution {
public int minimumEffort(int[][] tasks) {
Arrays.sort(tasks, (t1, t2) -> Integer.compare(t1[1] - t1[0], t2[1] - t2[0]));
int res = 0;
// 按次序回滚任务
for (int i = 0; i < tasks.length; i++) {
int[] task = tasks[i];
res = Math.max(res + task[0], task[1]);
}
return res;
}
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn), n n n是任务数,空间 O ( n ) O(n) O(n)。