P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
时间:2023.10.4
题目地址:合并果子
题目分析
了解题意后,要想取最优的,贪心策略就是每次取两个最小堆进行合并 ( 注意:合并后的堆也要考虑进去, 就是说合并后在考虑最小堆的时候也要考虑。 ) 。
有
n
n
n 堆的话只需排
n
−
1
n-1
n−1 次就行了,题目已经告诉你了。
我想到的就是两种思路:
-
先将数组排序,然后将前两个最小堆合并后,在删掉一个堆,然后再对这个数组进行排序,最后只剩一堆后输出。如下图分析。
看了数据量很可能超时,果然超时了,只有30分。
-
首先,还是对输入的数组进行排序,然后在开一个队列,放合并后的堆,通过比大小来判断堆的合并,保证了每次取的都是最小的两个,并且相较于上个方法省去了删堆和排序的时间。
新开的队列存的堆也是有序的,这应该没什么问题。然后就通过索引来跟进就行了,注意越界问题。
不出意外,都过了。
代码
- 30分超时代码
n = int(input())
nums = [int(i) for i in input().split()]
nums = sorted(nums) + [0]*(30001-n)
total = 0
while True:
j = 0
# 只剩一堆后输出
if j+1 >= n:
break
else:
# 合并
nums[j] = nums[j] + nums[j+1]
# 删堆
for k in range(j+1, n):
nums[k] = nums[k+1]
total += nums[j]
n -= 1
# 排序
for l in range(j, n-1):
if nums[l] > nums[l+1]:
nums[l], nums[l+1] = nums[l+1], nums[l]
print(total)
- 满分代码
n = int(input())
nums = [int(i) for i in input().split()]
nums.sort()
# 存放合并堆的队列
nums_2 = [float('inf') for _ in range(n)]
# 目标
total = 0
# 判断此时最小的两个堆的指向
i = 0
j = 0
# 趟次
k = 1
# 存放每次的合并堆的索引/指针
index = 0
while k < n:
# 防止越界
if i > n-1: # 此时nums没有堆了,取完了
temp = nums_2[j]
j += 1
else:
if nums[i] < nums_2[j]: # 每次取最小的
temp = nums[i]
i += 1
else:
temp = nums_2[j]
j += 1
# 防止越界
if i > n-1: # 此时nums没有堆了,取完了
temp += nums_2[j]
j += 1
else:
if nums[i] < nums_2[j]: # 每次取最小的
temp += nums[i]
i += 1
else:
temp += nums_2[j]
j += 1
nums_2[index] = temp
total += temp
k += 1
index += 1
print(total)
思考
为什么只有可能是 i i i 越界,而 j j j 不会越界呢?读者可以自己思考。