校招真题练习015 万万没想到之抓捕孔连顺(头条)

万万没想到之抓捕孔连顺

题目描述
我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工,我提议

1. 我们在字节跳动大街的N个建筑中选定3个埋伏地点。
2. 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。

请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。

注意:
1. 两个特工不能埋伏在同一地点
2. 三个特工是等价的:即同样的位置组合(A, B, C) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用

输入描述:
第一行包含空格分隔的两个数字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)
第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000)表示,从小到大排列(将字节跳动大街看做一条数轴)

输出描述:
一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模

 1 def main():
 2     line0 = list(map(int,input().strip().split(' ')))
 3     buildings = list(map(int,input().strip().split(' ')))
 4     N,D = line0[0],line0[1]
 5     if N < 3:
 6         print(0)
 7     else:
 8         dp =[0] * N
 9         if buildings[2] - buildings[0] <= D:
10             dp[2] = 1
11         i = 3
12         j = 0
13         while i < N:
14             while j <= i - 2:
15                 if buildings[i] - buildings[j] > D:
16                     j += 1
17                 else:
18                     break
19             dp[i] = (i - j) * (i - j - 1) // 2
20             i += 1
21         sums = sum(dp)
22         print(sums % 99997867)
23 
24 if __name__ == '__main__':
25     main()

算法思路:滑动窗口+动态规划+组合。

本题目有一定难度,是一道综合多种算法思想的题目。

首先分析题目是要计算"组合数",在一个距离不大于D,并且节点不少于3的子区间内,任取3个点,构成一个组合。

这个子区间从左向右移动。移动的过程中计算可以构成的新的组合数。

初始化dp,用于记录:以dp[i]为结尾(必定包含i节点),可构成的子区域的最多组合数。

既然i节点必然包含,那么只需考虑在这个节点之前的最大可能的子区域中,任取另外2个节点即可。

例如:在3个节点中任取2个节点,计算式为:3 * 2 // 2,等于3

     在4个节点中任取2个节点,计算式为:4 * 3 // 2,等于6

可归纳为第19行的公式。

窗口从左向右滑动到最右边,完成1次遍历,对dp求和,即为所求。

13行~18行为滑动窗口策略,i为右边界,j为左边界。每次计算是在[j,i-1]的闭区间内的所有节点任取2个节点。

猜你喜欢

转载自www.cnblogs.com/asenyang/p/11220277.html