要点:题目要求一个数,这个数的范围是1~n,故可以采用二分查找这个数。
1.假设现在这个数是k,即可以选择K个数可以用它们组成一个完美数列。我们做出这个判断的依据是题目中的条件,故只有这k个数中的最大最小值是要关心的。
2.现在将输入的序列由小到大排列,而1中所用到的这个最大值最大可以是序列的最后一个元素,若用数组存储的话便为data[n-1];最小只能是data[k-1],这里的k指的是选的数。
3.选好最大值后便找能作为这k个数的最小值进行验证,可以知道下标从0~i-mid+1的数都可以作为最小值,其中的i为最大值的下标。
故代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;
const int maxn = 1e5+10;
typedef long long int ll;
ll p,data[maxn];
int n;
int main()
{
int i,flag,j;
cin>>n>>p;
for(i=0;i<n;i++)
cin>>data[i];
sort(data,data+n);
int left = 1,right = n,mid,_max = 0;
while(left<=right)
{
flag = 0;
mid = (left+right)/2;
for(i=n-1;i>=mid-1;i--)
{
for(j=i-mid+1;j>=0;j--)
{
if(data[i] <= data[j] * p)
{
flag = 1;
break;
}
}
if(flag == 1)
break;
}
if(flag == 1)
{
left = mid + 1;
if(_max < mid)
_max = mid;
}else
right = mid - 1;
}
cout<<_max;
return 0;
}
然而超时了,不难看出其实没必要从下标0~i-mid+1的数据挨个检查,因为在检查这个最小值时最大值是确定的,p也是确定的,故检查最大的那个数即可,即下标为i-mid+1的那个数。若此数不满足那其他的更不满足了。
改进后:
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
typedef long long int ll;
ll p,data[maxn];
int n;
int main()
{
int i,flag,j;
cin>>n>>p;
for(i=0;i<n;i++)
cin>>data[i];
sort(data,data+n);
int left = 1,right = n,mid,_max = 0;
while(left<=right)
{
flag = 0;
mid = (left+right)/2;
for(i=n-1;i>=mid-1;i--)
{
if(data[i] <= data[i-mid+1] * p)
{
flag = 1;
break;
}
}
if(flag == 1)
{
left = mid + 1;
if(_max < mid)
_max = mid;
}else
right = mid - 1;
}
cout<<_max;
return 0;
}