题目:瞌睡
小易觉得高数课太无聊了,决定睡觉。不过他对课上的一些内容挺感兴趣,所以希望你在老师讲到有趣的部分的时候叫醒他一下。你知道了小易对一堂课每分钟知识点的感兴趣程度,并以分数量化,以及他在这堂课上每分钟是否会睡着,你可以叫醒他一次,这会使得他在接下来的k分钟内保持清醒。你需要选择一种方案最大化小易这堂课听到的知识点分值。
输入描述:
第一行 n, k (1 <= n, k <= 105) ,表示这堂课持续多少分钟,以及叫醒小易一次使他能够保持清醒的时间。
第二行 n 个数,a1, a2, … , an(1 <= ai <= 104) 表示小易对每分钟知识点的感兴趣评分。
第三行 n 个数,t1, t2, … , tn 表示每分钟小易是否清醒, 1表示清醒。
输出描述:
小易这堂课听到的知识点的最大兴趣值。
输入例子1:
6 3
1 3 5 2 5 4
1 1 0 1 0 0
输出例子1:
16
最初写了两层暴力循环,计算在不同时间叫醒的兴趣评分
import sys
line1 = sys.stdin.readline().split()
line1 = list(map(int, line1))
n, k = line1[0], line1[1]
a = sys.stdin.readline().split()
a = list(map(int, a))
t = sys.stdin.readline().split()
t = list(map(int, t))
knowledge = []
if k>=n:
print(sum(a))
else:
for i in range(n-k):
grade = 0
for j in range(i):
if t[j] ==1:
grade+=a[j]
for j in range(i,i+k):
grade += a[j]
for j in range(i+k,len(a)):
if t[j] == 1:
grade +=a[j]
knowledge.append(grade)
print(max(knowledge))
结果…
您的代码已保存
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
case通过率为60.00%
那咋算呢,这回算 长度为k 的窗口内,叫醒比不叫醒增加的兴趣分吧。因为醒着的分数是固定的。分数能得到提升是因为叫醒增加的。找到叫醒增加最多的就好了
import sys
line1 = sys.stdin.readline().split()
line1 = list(map(int, line1))
n, k = line1[0], line1[1]
a = sys.stdin.readline().split()
a = list(map(int, a))
t = sys.stdin.readline().split()
t = list(map(int, t))
# 醒着的得分
base_score =0
for i in range(n):
base_score += a[i]*t[i]
# 叫醒增益
gain = 0
if k >= n:
print(sum(a))
else:
for i in range(n):
# 找到叫醒的点
g = 0
if t[i] ==0:
# 开始计算叫醒的增益
for j in range(i,min(i+k,n)):
if t[j] ==0:
g+=a[j]
if g>gain:
gain = g
print(gain+base_score)
结果…比上次进步了一点点:
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
case通过率为80.00%
所以算法的问题是太慢了。。
O
(
n
2
)
O(n^2)
O(n2)肯定是不行的。。
接下来想办法写
O
(
n
)
O(n)
O(n)
牛客上讨论的一个答案是这样的, 这个和下面的答案意思差不多。但下面那个更简洁好懂一些。
nk = list(map(int,input().split()))
n = nk[0]
k = nk[1]
a = list(map(int,input().split()))
t = list(map(int,input().split()))
max = 0 #记录前k个瞌睡叫醒后兴趣的最大值
for i in range(k):
if(t[i]==0):
max += a[i]
pos = 0 #记录最大值的位置
pre = 0 #记录窗口移动过程中最后一个元素位置( 应该是窗口最前面的一个元素的位置,就是移动就要被淘汰的元素)
cur = max #当前窗口内的兴趣值
for i in range(k,n):
if(t[pre]==0): #元素过期 前一个叫醒点
cur -= a[pre] # 当前窗口减去淘汰值
if(t[i]==0): # 新的叫醒点
cur += a[i] #当前窗口加入新的值
pre += 1 # 窗口后移了一位
if(cur>max): # 更新最大值
max=cur
pos=i
sum=0 #记录清醒状态下的和
for i in range(n):
if(t[i]==1):
sum+=a[i]
print(sum+max)
(出于规范,建议不要用sum,和max做变量名吧)
下面这个代码比较好懂!
n, k = list(map(int, input().strip().split()))
a = list(map(int, input().strip().split()))
t = list(map(int, input().strip().split()))
base_score = 0
for i in range(n):
if t[i]:
base_score += a[i]
a[i] = 0 # 计算完醒着的得分,把得分置为0,这样就不用费力气去找叫醒点。大小为k的窗口每次后移一个就好,醒着的值为0不影响
max_score = tmp = sum(a[:k]) # max_score记录叫醒增益。 tmp记录当前窗口的增益
for i in range(k, n): # 第二个开始的窗口的末尾范围是 k~n-1
tmp = tmp + a[i] - a[i - k] # 后移了一位,新窗口增益是 旧tmp + 新增的值a[i] - 过期的值a[i-k]
max_score = max(max_score, tmp) # 如果右移一位增益变大,则更新max_score
ans = base_score + max_score # 最终得分就是醒着的得分加叫醒增益
print(ans)