【题意】
给出N个10^5以下的正整数组成的序列,找出能使M <= m*p成立的最大序列元素数,其中M是目标序列的最大值,m为最小值。
【思路】
考虑到时间限制为200ms,N的最大值为10^5 所以时间复杂度不能超过 10^6量级即O(nlogn)的复杂度。
-
首先对序列进行排序,复杂度O(nlogn)
-
然后用two pointers两个指针分别指向最大值和最小值,然后判断M <= m*p是否成立,然后依次向中心移动两个指针直到第一个成立的地方然后退出,如果两点相遇都没有成立,输出0。
按照上面的思想写出来的代码最后一个测试点总过不去,开始怀疑是边界值的问题,但是再在特判边界值后还是过不去。因此怀疑是算法有漏洞。
仔细一想找出了原因和反例:假设i = 0, j = n- 1不能使上式成立,那么由于我的算法总是会先让i++,那么接下来判断的就是i = 1, j = n- 1,如果它也不能使上式成立则j++下一步比较的将是i = 1, j = n- 2.但是,假设i = 0, j = n - 1能使上式成立的话,那么我对算法就会忽略上面这种情况。
正确算法
考虑到如果a[j] <= a[i] * p 成立那么对[i,j]内的任意位置k a[k] <= a[i] * p 一定成立。让i和j都从最小值(左端)开始,让i不动,j不断右移直到不满足上式为止,更新这个过程中的最大长度。然后i右移一位,再让j从当前位置开始不断右移注意这里j不用再从i开始向右的原因就是刚刚说的。
【代码】
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 100010;
int main() {
int p, n;
int s[maxn];
scanf("%d%d", &n, &p);
for (int i = 0; i < n; i++)
{
scanf("%d", &s[i]);
}
sort(s, s + n);
int i = 0, j = 0, maxl = 0;
while (i < n && j < n)
{
while (j < n && (long long)s[i] * p >= s[j])
{
maxl = max(maxl, j - i + 1);
j++;
}
i++;
}
printf("%d\n", maxl);
return 0;
}