思路: 核心:尺取(双指针)
五种等地成绩其实没用,题目中只对A成绩限制人数,所以成绩分为A成绩和非A成绩。
求最大和最小值的最小差,肯定要排序,按照什么排序呢?
第一关键字自然是分数高低,第二关键字按照是否为A排序。
一个学生选了A成绩,会对后面学生成绩的选择限制(能选A的人少一个),所以能选非A的尽量非选A成绩,给后来者留更多的选择余地。
所谓尺取,右指针是无需往左移动的,因为往左题意反而会不满足(选的人会少)。
这题比较麻烦的是要统计的变量比较多,写的时候耐心一点。
//尺取
#include <bits/stdc++.h>
#define IOS std::ios_base::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
using namespace std;
typedef long long ll;
const int N=1e5+10;
int numstu=0,numa=0; //选中学生数 仅有A成绩的学生
int stu[N],sa[N];//每个学生被选中的成绩数 每个学生的A成绩是否被选
struct node
{
int b,val,num; //b是否为A分数,val分数,num学生
}t[N*5];
bool cmp(node a, node b)//先取非A的分数,给后面的分数留更多的选择余地
{
if( a.val != b.val) return a.val<b.val;
else return a.b<b.b;
}
int main()
{
int n,k;
cin>>n>>k;
for(int i=1 ;i<=n ;i++)
{
cin>>t[i].val;t[i].b=1;t[i].num=i;
cin>>t[i+n].val;t[i+n].num=i;
cin>>t[i+2*n].val;t[i+2*n].num=i;
cin>>t[i+3*n].val;t[i+3*n].num=i;
cin>>t[i+4*n].val;t[i+4*n].num=i;
}
sort(t+1,t+1+5*n,cmp);
// 尺取
int ans=0x7f7f7f7f;
int j=1;
//第一个数据入“队”
stu[t[1].num]++;
numstu++;
if( t[1].b==1 ) {
numa++;sa[t[1].num]++;}
for(int i=1 ;i<=5*n ;i++)
{
while(j<=5*n)
{
//尺取结束条件
if(numa<=k && numstu==n)
{
ans= min(ans,t[j].val-t[i].val);
break;
}
//右指针只要往右走
j++;
stu[t[j].num]++;//学生成绩数++
if( stu[t[j].num]==1 ) numstu++; //如果成绩是第一次选到,学生++
if( t[j].b==1 )//如果是A成绩
{
sa[t[j].num]++;
if(stu[t[j].num]==1 ) numa++;
}
}
stu[t[i].num]--;
//如果减去后学生成绩数为0,选中学生数--,如果是A成绩,numa--
if(stu[t[i].num]==0 )
{
numstu--;
if( t[i].b==1 ) {
numa--;sa[t[i].num]--;}
}
if(stu[t[i].num]==1 && sa[t[i].num]==1 ) numa++;
}
cout<<ans<<"\n";
}