LeetCode-1962-移除石子使总数最小
题目信息:
给你一个整数数组
p
i
l
e
s
piles
piles ,数组 下标从
0
0
0 开始 ,其中
p
i
l
e
s
[
i
]
piles[i]
piles[i] 表示第
i
i
i 堆石子中的石子数量。另给你一个整数
k
k
k ,请你执行下述操作 恰好
k
k
k 次:
选出任一石子堆
p
i
l
e
s
[
i
]
piles[i]
piles[i] ,并从中 移除
f
l
o
o
r
(
p
i
l
e
s
[
i
]
/
2
)
floor(piles[i] / 2)
floor(piles[i]/2) 颗石子。
注意:你可以对 同一堆 石子多次执行此操作。
返回执行
k
k
k 次操作后,剩下石子的 最小 总数。
注:
f
l
o
o
r
(
x
)
floor(x)
floor(x) 为 小于 或 等于
x
x
x 的 最大 整数。(即,对
x
x
x 向下取整)。
- 示例1:
输入: p i l e s = [ 5 , 4 , 9 ] , k = 2 piles = [5,4,9], k = 2 piles=[5,4,9],k=2
输出:12
解释:可能的执行情景如下:
- 对第 2 堆石子执行移除操作,石子分布情况变成 [5,4,5] 。
- 对第 0 堆石子执行移除操作,石子分布情况变成 [3,4,5] 。
剩下石子的总数为 12 。
- 示例2:
输入: p i l e s = [ 4 , 3 , 6 , 7 ] , k = 3 piles = [4,3,6,7], k = 3 piles=[4,3,6,7],k=3
输出:12
解释:可能的执行情景如下:
- 对第 2 堆石子执行移除操作,石子分布情况变成 [4,3,3,7] 。
- 对第 3 堆石子执行移除操作,石子分布情况变成 [4,3,3,4] 。
- 对第 0 堆石子执行移除操作,石子分布情况变成 [2,3,3,4] 。
剩下石子的总数为 12 。
提示:
- 1 < = p i l e s . l e n g t h < = 1 0 5 1 <= piles.length <= 10^5 1<=piles.length<=105
- 1 < = p i l e s [ i ] < = 1 0 4 1 <= piles[i] <= 10^4 1<=piles[i]<=104
- 1 < = k < = 1 0 5 1 <= k <= 10^5 1<=k<=105
相关标签 :贪心,数组,堆(优先数列)
题解
方法1:贪心法(超时)
初见这道题,一阵开心,这不简单?在 k k k次循环中每次找到当前值最大的下标,对其进行 f l o o r ( x ) floor(x) floor(x)操做,丢掉 p i l e s [ m a x piles[max piles[max_ i n d e x ] index] index]整除 2 2 2 个石子。在完成循环后,得到的 p i l e s piles piles 数组的和即为我们在找的剩余石子的数量。
实现代码(Python)
class Solution:
def minStoneSum(self, piles: List[int], k: int) -> int:
if piles is None:
return 0
for i in range(k):
maximum = max(piles)
Max_index = piles.index(maximum)
if piles[Max_index] % 2 == 0 :#如果当前值为偶数直接整除
piles[Max_index] = piles[Max_index] // 2
else :#如果当前值为偶数奇数则向下取整
piles[Max_index] = piles[Max_index]//2 + 1
print(piles)
return sum(piles)
遗憾的是,并未通过全部的测试用例。定睛一看,本算法的时间复杂度为
O
(
k
∗
l
e
n
g
t
h
)
O(k*length)
O(k∗length) , 而
1
<
=
p
i
l
e
s
.
l
e
n
g
t
h
<
=
1
0
5
1 <= piles.length <= 10^5
1<=piles.length<=105 ,
1
<
=
k
<
=
1
0
5
1 <= k <= 10^5
1<=k<=105,则如果
k
k
k和
l
e
n
g
t
h
length
length均取最大值的话,确实最终的计算次数是超出最大的计算能力的。唯有想办法优化。
方法2:贪心法+优先队列(大根堆)
首先介绍一下什么是优先队列:优先队列是一种抽象数据结构,在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构(大根堆)来实现。(不了解大根堆的朋友可以自行百度相关知识)
根据题目描述,为了使得剩下的石子总数最小,我们需要尽可能多地移除石子堆中的石子。因此,每次应该贪心地选择数量最多的石子堆进行移除。
我们创建一个优先队列(大根堆)
h
e
a
p
heap
heap ,用于存储石子堆的数量。初始时,将所有石子堆的数量加入优先队列。
接下来,我们进行
k
k
k 次操作。在每一次操作中,我们取出优先队列的堆顶元素
x
x
x,将
x
x
x 减半后重新加入优先队列。
在进行了
k
k
k 次操作后,优先队列中所有元素的和即为答案。
实现代码(Python)
class Solution:
def minStoneSum(self, piles: List[int], k: int) -> int:
heap = [-x for x in piles]
heapify(heap)
for _ in range(k):
heapreplace(heap, heap[0] // 2)
return -sum(heap)
复杂度分析:
因为大根堆的插入时间复杂度为 O ( l o g 2 n ) O(log_2 n) O(log2n),所以该算法总的复杂度为:
- 时间复杂度 O ( n + k × l o g 2 n ) O(n+k×log_2 n) O(n+k×log2n)
- 空间复杂度 O ( n ) O(n) O(n)
其中 n n n 是数组 p i l e s piles piles 的长度。
题记:
- 研究生在读,我会尽量保持LeetCode每日一题的思路和代码输出。希望大家多多支持。
- 水平有限,希望各位大佬能够批评指正。您的教诲是我进步的船帆。
- 希望各位跟我一样的小白能跟我一起参与到做题和讨论中来。共同进步是我所能期盼的最高愿想。
- 您的点赞和关注是我坚持分享的动力泉源,希望能将这件简单平凡的事一直做下去。感谢大家。