1030 完美数列 (25 分)
题意描述:
给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。
现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。
输入格式:
输入第一行给出两个正整数 N 和 p,其中 N(≤10^5 )是输入的正整数的个数,p(≤10^9 )是给定的参数。第二行给出 N 个正整数,每个数不超过 10^9 。
输出格式:
在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。
输入样例:
10 8
2 3 20 4 5 1 6 7 8 9
输出样例:
8
解题思路:
根据题意,我们可以把题目的描述改为如下的形式:
给定一个升序数组,(我们可以对输入的数组排序得到一个升序数组),从数组中挑出连续的(连续是一个隐藏的条件,因为要想让子数组的长度最长,就要让min 和 max组成的区间覆盖原数组中尽可能多的元素,把min 到 max的所有元素都放置到子数组中就是连续的。)一部分组成一个子数组,这个子数组要满足 max <= min * p
。 求所有满足要求的最长子数组的长度。
这道题目可以用动态规划的思路来求解,时间复杂度为O(n) ,这样应该就不会超时了。
我们用一个数组dynamic_planning
来记录输入数组data
中的每一个元素作为 一个完美数列的最大值的时候 这个完美数列的长度。如dynamic_planning[1]
表示以data[1]
作为某个完美数列的最大值的时候这个数列的长度。
当程序执行到 data[x]
的时候, 所有以data[x]
左边的元素作为最大值的完美数列的长度都已经求解出来了。那么对于以data[x]
为最大值的完美数列有两种可能:
- 这个完美数列是
data[x-1]
的那个完美数列的扩展,这两个完美数列有着相同的最小值。此时dynamic_planning[x] = dynamic_planning[x-1] + 1
- 这个完美数列和
data[x-1]
的那个完美数列完全不同,最大值不同,最小值也不同。这时,我们需要找到一个新的最小值,然后求出这个最小值到data[x]
这个最大值之间的元素数量。
最后,我们已经求出了所有可能的完美数列的长度,只要返回其中的最大值即可。
代码:
def main():
N, p = (int(x) for x in input().split())
# 元组拆包
data = [int(x) for x in input().split()]
# 接收输入的N个数字为整数列表
data.sort()
# 对输入的数字排序,排序结果为从小到大
dynamic_planning = [1 for x in range(N)]
# dynamic_planning用来记录data中的每一个元素作为 一个完美数列的最大值的时候 这个完美数列的长度。
# 如dynamic_planning[1] 表示以data[1] 作为某个完美数列的最大值的时候这个数列的长度。
minn_index = 0
# 当前的完美数列的最小值下标
minn = data[minn_index]
# 当前完美数列的最小值
for x in range(1, N):
if data[x] <= p * minn:
# 如果data[x]所在的完美数列和data[x-1]所在的完美数列有着相同的最小值
dynamic_planning[x] = dynamic_planning[x - 1] + 1
else:
# 否则找到一个与data[x]相适应的,新的最小值来构造完美数列
while data[x] > p * minn:
# 最小值不断增大,直到 data[x] <= p * M,直到满足完美数列的约束。
minn_index += 1
minn = data[minn_index]
dynamic_planning[x] = x - minn_index + 1
# 然后更新,新的完美数列的长度。看,因为data是我们已经排过序的结果,所以要把min 到 max 的所有元素都放进来,只需要计算下标即可。
# 这一段可能不好理解,建议大家手动执行一下就能体会动态规划的魅力了。
# print(data)
# print(dynamic_planning)
print(max(dynamic_planning))
# 输出所有完美数列的最大长度即可
if __name__ == '__main__':
main()
易错点:
- 直接暴力去做可能会超时。
总结:
- 存储中间结果,减少重复计算是动态规划的魅力所在。
另:
四月将尽,项目受阻,心力交瘁,身心俱疲。后续题目将改为不定期更新,下次更新时间待定。 | |
---|---|