题目链接:点这里~
题目大意
n个同学考试,需要去给每位同学评定等级,一共有五个等级,但是对于每个同学来说,每个等级分别对应五个不同考试分数:ai, bi, ci, di, ei
现在有要求,评定A的同学数量不能超过k,问最终考试成绩最大值和最小值的差值最小是多少?
思路
每个分数对应一个同学id、一个评定等级,把这些n*5个数据按照分数从小到大排序,使用双指针(尺取法)计算答案
需要满足的条件是选的分数能够对应n个同学,评A等级的数量不超过k,所以能不选A就尽量不选。
如果说该区间里面某个同学出现了不止一个分数,其中有一个是评A的分数,那么可以不评A的而是其他的。但是如果说该同学只有一个分数,并且是评A的分数,那么只能评A。
ac代码
#include<bits/stdc++.h>
using namespace std;
#define io cin.tie(0);ios::sync_with_stdio(false);
#define debug(x) cout<<#x<<"="<<x<<endl
#define lowbit(x) x&(-x)
#define pii pair<int,int>
#define mk make_pair
#define ll long long
#define rs p<<1|1
#define ls p<<1
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline ll read(){
ll p=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){p=(p<<1)+(p<<3)+(c^48),c=getchar();}
return f*p;
}
struct node{
int id, score, rank; //编号 分数 等级
bool operator < (const node &a) const{
return score < a.score;
}
}a[maxn];
map<int, int> mp; //区间存同学分数个数
int vis[maxn]; //区间同学是否有评A的
int cnt, n, k; //cnt评A个数
void add(int x){ //区间加值
++ mp[a[x].id];
if(a[x].rank){
if(mp[a[x].id] == 1) cnt ++; //如果该同学只有一个A的分数,那么只能评A
vis[a[x].id] = 1;
} else if(mp[a[x].id] == 2 && vis[a[x].id]) cnt --; //如果说有A但是数量刚好是2,说明之前评A,那么现在可以不评A了
}
void del(int x){ //区间去值
if(a[x].rank){
if(mp[a[x].id] == 1) cnt --; //只有一个A的分数,那么评A的就要少一个了
vis[a[x].id] = 0;
} else if(mp[a[x].id] == 2 && vis[a[x].id]) cnt ++; //如果说有A但是数量刚好是2,说明去掉该分数之后只剩下一个A了,那么下一个要评A了
if(-- mp[a[x].id] == 0) mp.erase(a[x].id);
}
bool ok(){ //区间标准: n个同学 A不超k
return mp.size() == n && cnt <= k;
}
void solve(){
int m = 0;
cin >> n >> k;
for(int i = 1; i <= n; i ++){
int x; cin >> x;
a[++m] = {i, x, 1};
for(int j = 1; j < 5; j ++){
cin >> x;
a[++m] = {i, x, 0};
}
}
sort(a + 1, a + m + 1);
int r = 1, ans = inf;
for(int l = 1; l <= m; l ++){
if(l - 1) del(l - 1);
while(!ok() && r <= m) add(r ++);
if(ok()) ans = min(ans, a[r - 1].score - a[l].score);
}
cout << ans << endl;
}
int main(){
io;
solve();
return 0;
}