我叫王大锤,是一名特工。我刚刚接到任务:在字节跳动大街进行埋伏,抓捕恐怖分子孔连顺。和我一起行动的还有另外两名特工,我提议
- 我们在字节跳动大街的N个建筑中选定3个埋伏地点。
- 为了相互照应,我们决定相距最远的两名特工间的距离不超过D。
我特喵是个天才! 经过精密的计算,我们从X种可行的埋伏方案中选择了一种。这个方案万无一失,颤抖吧,孔连顺!
……
万万没想到,计划还是失败了,孔连顺化妆成小龙女,混在cosplay的队伍中逃出了字节跳动大街。只怪他的伪装太成功了,就是杨过本人来了也发现不了的!
请听题:给定N(可选作为埋伏点的建筑物数)、D(相距最远的两名特工间的距离的最大值)以及可选建筑的坐标,计算在这次行动中,大锤的小队有多少种埋伏选择。
注意:
- 两个特工不能埋伏在同一地点
- 三个特工是等价的:即同样的位置组合(A, B, C) 只算一种埋伏方法,不能因“特工之间互换位置”而重复使用
输入描述:
第一行包含空格分隔的两个数字 N和D(1 ≤ N ≤ 1000000; 1 ≤ D ≤ 1000000)
第二行包含N个建筑物的的位置,每个位置用一个整数(取值区间为[0, 1000000])表示,从小到大排列(将字节跳动大街看做一条数轴)
输出描述:
一个数字,表示不同埋伏方案的数量。结果可能溢出,请对 99997867 取模
输入例子1:
4 3
1 2 3 4
输出例子1:
4
例子说明1:
可选方案 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)
输入例子2:
5 19
1 10 20 30 50
输出例子2:
1
例子说明2:
可选方案 (1, 10, 20)
最直接的方法,三个for循环,但这肯定不行,时间复杂度太高了。
其实觉得这道题是一个排列组合问题,在合理的区间取出3个数进行组合。
比如第一个例子,1 2 3 4,是符合题意的,然后来一个公式,
就可以得出结果了。
所以我们找出合理的区间计算方案并加起来就行,但代码写起来会有一些改变,请往下看。
方法一:(这个方法会超时(牛客网提交得11.2分),但我觉得相对来说会比较容易理解一点)
import java.util.Scanner;
public class Main{
public static void main(String [] args){
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
int D=sc.nextInt();
int[] coordinate=new int[N];
for(int i=0;i<N;i++){
coordinate[i]=sc.nextInt();
}
Long count=0L;
/**
* 先从第一位开始,把它看为区间左端点,判断它的最大合理区间在哪里,判断条件是coordinate[j]-coordinate[left]
* 如果coordinate[j]-coordinate[left]是<=D的话,说明里面数都是符合题目的。
* 等循环找到最大区间后就计算方案,然后继续从第二位为区间左端点开始找。
* 此时有一个问题,有些会重复,比如说 1 2 3 4 这个例子,1为左区间时,有(2,3,4)这个组合
* 2为左区间时,也有这个组合。所以我们换一下思路,当1为区间左端点时,在它的合理区间(除去1),取两个数
* 和1组合,此时有Cn2种组法。
*/
for(int left=0;left<N-2;left++){
int right=0;
for(int j=left+2;j<N;j++){
//这里是找left的最大合理区间
if(coordinate[j]-coordinate[left]>D){
break;
}
right=j;
}
if(right-left>=2){
//right-left是算除了lfet的区间数量
//这个公式是计算Cn2的,把Cn2的公式化简就成了n*(n-1)/2
//要用long(这个我不是很懂,网上看到的)
count+=(long)(right-left)*(long)((right-left)-1)/2;
}
}
System.out.println(count%99997867);
}
}
方法二:(参考网上的,自己改进了一点点)
其实思想差不多,说是用了滑动窗口这种方法(我也不是很理解,,)
我觉得这里主要是把区间的右端点(注意,是右端点,如果是左端点的话感觉会有问题的,虽然答案是一样)做固定点,比如说一开始是三个,1 2 3,以3为固定点,从 1 2里组合,只有一种,然后到 1 2 3 4 了,以4为固定点 ,从 1 2 3取两个组合,有3种,加起来就是4种了。
我之前一开始有一个地方是理解不了的,因为没有明确固定一个点(上面的方法有这个思想),后来想想,应该是有固定点的,不过是隐藏了,因为代码是算合理区间的数量-1的(本来合理区间的数量是右下标-左下标+1的,代码没有+1,所以应该是把那个1作为固定点了,我理解那个固定点就是区间右端点)。
import java.util.Scanner;
public class Main{
public static void main(String [] args){
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
int D=sc.nextInt();
int[] coordinate=new int[N];
for(int i=0;i<N;i++){
coordinate[i]=sc.nextInt();
}
Long count=0L;
int left=0;
int right=2;
while(right<N&&left<right) {
if(coordinate[right]-coordinate[left]>D) {
left++;
}else if (right-left<2) {
right++;
}else {
int len=right-left;
count+=(long)(len)*(long)(len-1)/2;
right++;
}
}
System.out.println(count%99997867);
}
}
最后,因为知道自己的表达能力和理解能力比较弱,如果有什么错误的话,希望大家能多包容,多指出我的错误,谢谢。