目录
这道题做得太艰难了……我做了一晚上……
所以兄弟们,刚上手算法题可能都很艰难,大家不要放弃一起加油哇!
蓝桥杯真题——k倍区间
1.题目描述
示例
输入:
5 2 1 2 3 4 5
输出:
6
2.思路过程详细注释解析
ps:非最终代码,运行会超时
"""
本题考点:前缀和 + 数学思维
本题乍一看是考前缀和,
但由于数据过大,所以还需做亿点优化。
主要优化为:(数学技巧)发现余数和k倍区间的关系(详见后)
"""
N, K = map(int, input().split())
A = []
for _ in range(N):
A.append(int(input()))
A_pre = [0]
for i in range(N):
A_pre.append(A_pre[i] + A[i])
ans = 0
'''
今日get——思维转换:
当我们使用二重循环导致时间复杂度过大引起运行超时时,
很多情况下我们需要 多观察分析题目条件 ,
从 数学角度 上灵活优化代码,
而不是仅仅想着:
"二重循环太大了想办法弄成一重的"
一直死磕代码本身,可能并不会有成效
'''
# for i in range(0, N + 1):
# for j in range(i + 1, N + 1):
# if (A_pre[j] - A_pre[i]) % K == 0:
# ans += 1
'''
# 数学技巧:发现余数和k倍区间的关系
(A_pre[j] - A_pre[i]) % K == 0
==> A_pre[j] % K == A_pre[i] % K
即:当 A[j]的前缀和的余数 == A[i]的前缀和的余数 时,
区间[i, j]是K倍区间(ans += 1)
因此,我们有了新的思路:
1.求出所有前缀和的余数存入列表 A_pre_yu
2.遍历列表A_pre_yu,两个余数相同的位置可以构成一个k倍区间
相同余数个数 区间个数
2 1
3 2+1
4 3+2+1
……
可以发现,区间个数的运算满足等差数列求和
(等差数列前n项和公式:Sn=n*a1+n(n-1)d/2或Sn=n(a1+an)/2)
# 也可直接使用组合数公式:C(n, 2)
# 可以这么想:比如相同的余数有4个,求区间个数。
# 就是在问你:从4个人里挑2个出来唱歌,请问有几种组合方式?
当有相同余数时,我们按照上述规律统计区间个数ans。
3.A_pre_yu的第一个元素0刚好不影响结果,
所以不需要额外考虑。
'''
A_pre_yu = [i % K for i in A_pre]
for i in range(K):
cnt = A_pre_yu.count(i) # 后期发现运行依旧超时,所以最终版会对此进行优化
# ans += (cnt - 1) * (1 + (cnt - 1)) // 2 # 前n项和
ans += (cnt - 1) * cnt // 2 # 组合数 C(n, 2)
print(ans)
# 此思路或非最优解,还请见谅
3.最终代码实现
"""
针对过程代码的两处改进:
1.优化对数列输入、前缀和及其余数的存储
2.优化count函数,改用列表存储余数个数
"""
N, K = map(int, input().split())
A_pre, ans = 0, 0
cnt_yu = [0] * K # 存储余数的个数:下标是余数,值是余数个数
for _ in range(N):
A_pre += int(input())
cnt_yu[A_pre % K] += 1 # 对该余数个数+1
cnt_yu[0] += 1
for i in range(K):
ans += cnt_yu[i] * (cnt_yu[i] - 1) // 2
print(ans)
# 此思路或非最优解,还请见谅
题解和分析是结合自己的认识以及参考各路大神的代码整理汇总出来的,供大家参考~
如有不足或不解之处欢迎留言指正哈。
如有帮助可以点赞收藏嘛,感谢~