题目描述
我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕*孔连顺。和我一起行动的还有另外两名特工,我提议
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个节点。