题意:
给定一个数组A,找出一个范围[x,y],把数组A恰好分为K块,使得每块中在范围[x,y]中的数>在范围外的数,并且最小化y-x,输出k个区间的左右端点
分析:
- 寻找一个范围[x,y]
- 然后运用结论用[x,y]分割区间
- 输出[x,y]
找到一个范围[x,y]
我们可以贪心的想,要找到k个数组,其实可以让每个数组在[x,y]的数in-在[x,y]外的数out>=1
问题转化为了 : 找到k个区间,每个区间内在[x,y]的数>=在[x,y]外的数+1
记f(1,n) 为 in - out
- in + out ==n
- in - out >=k
那么,解方程组得 in*2>=n+k in>=[n+k]/2 (上取整),通过排序数组后,不断枚举[i,i+in]区间,然后最小化两端点就可以了
分割区间
参考
牛客寒假训练营 1 F中位数切分(结论)_qq12323qweeqwe的博客-CSDN博客
记 f(l,r) 为一个区间内在[x,y]内的数 - 在[x,y]外的数
那么如果f(l,r)>1 一定可以分为两个区间 f(l,mid)>0 &&f(mid+1,r) >0;
这题不是要求最多的段数,而是要求恰好的段数,并且输出,所以划分到刚好k段就可以了,每次划分都会+1,最后需要划分k-1次
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+10;
int a[N];
int b[N];
int l,r;
int in(int x)
{
return l<=x&&x<=r?1:-1;
}
void solve()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i];
}
sort(b+1,b+n+1);
//寻找x,y
int len=n+k+1>>1;
int res=0x3f3f3f3f;
for(int i=1;i+len-1<=n;i++)
if(res>b[len+i-1]-b[i])
{
l=b[i],r=b[len+i-1];
res=r-l;
}
//输出范围
cout<<l<<' '<<r<<"\n";
int cnt=0;
//把前面的段尽可能地分割
for(int i=1;i<=n;i++)
{//运用f(l,r)来分割数组
cout<<i<<' ';
int cc=in(a[i]);
while(cc<=0)
cc+=in(a[++i]);
if(++cnt!=k) cout<<i<<'\n';
else break;
}
//输出最后一段,最后一段尽可能地长,所以直接到n
cout<<n<<'\n';
}
int main()
{
int _t;
cin>>_t;
while(_t--)
solve();
return 0;
}