题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=6231
题意:
输入n,k,m,需要求在n个数的a数组中,任意区间里选择第k大的数存入b数组,在b数组中求得第m大的数
解:
其实一开始没懂怎么去解这道题,但可以反过来想想,如果我是b数组中一个数如何满足题目所要求的条件 ?
首先将a数组存入b数组,且将b数组排序,二分下标得到b[mid],判断b[mid]在a数组中的任意区间里有多少种情况存在那么一个第k大的数,
如果存在的情况大于m,那么该数偏小;若小于m,则该数偏大;
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
//先在b数组(b数组为a数组中的各项元素且排好序)中二分一个数x
//将x放置在a中进行比较查找,找到第k大的数记录位置后,用n减去该下标
//因为区间任意,所以k的可能性就有n-r的累加
//最后结果如果比m大,那么该x偏小;如果比m小,那么该x偏大
//结果与m比较的意义在于,k的存在数目比m大的话表明有足够的k拼凑出m;
const int maxn = 1e5+20;
typedef long long ll;
ll a[maxn],b[maxn];
ll k,m,n;
ll judge(ll x)
{
ll num=0;
ll l=1,r=0;
ll sum=0;
while(r<=n)
{
if(num<k)
{
if(a[r+1]>=x)num++;
r++;
}
else
{
if(num==k)sum+=n-r+1;
if(a[l]>=x)num--;
l++;
}
}
return sum;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d%I64d",&n,&k,&m);
for(int i=1;i<=n;i++)
{scanf("%I64d",&a[i]);b[i]=a[i];}
sort(b+1,b+1+n);
int l=1,r=n;ll ans=0;
int mid;
//cout<<"1"<<endl;
while(r>=l)
{
mid=(r+l)/2;
//cout<<"3"<<endl;
if(judge(b[mid])>=m)
{
ans=b[mid];
l=mid+1;
}
else
r=mid-1;
}
//cout<<"2"<<endl;
printf("%I64d\n",ans);
}
return 0;
}