洛谷 P10710 [NOISG 2024 Prelim] School Photo 题解
免责声明:此题解的思路来自洛谷的Moya_Rao
完整题目
P10710 [NOISG 2024 Prelim] School Photo
题目背景
翻译自 NOI SG 2024 Prelim C.School Photo。
题目描述
Zane 是 NOI 学校的校长。NOI 学校有 n n n 个班,每个班有 s s s 名同学。第 i i i 个班中的第 j j j 名同学的身高是 a i , j a_{i,j} ai,j。
现在 Zane 想从每个班上选出一名同学拍照,使得这 n n n 名同学中最高的同学和最低的同学的身高差最小。
请你输出这个最小值。输入格式
第一行,两个整数 n , s n,s n,s;
接下来 n n n 行,每行 s s s 个整数,表示 a a a。输出格式
一行一个整数表示答案。
输入输出样例 #1
输入 #1
2 3 2 1 8 5 4 7输出 #1
1输入输出样例 #2
输入 #2
3 3 3 1 4 2 7 18 9 8 10输出 #2
4说明/提示
【样例 #2 解释】
选择 a 1 , 3 , a 2 , 2 , a 3 , 2 a_{1,3},a_{2,2},a_{3,2} a1,3,a2,2,a3,2,答案为 8 − 4 = 4 8-4=4 8−4=4。
【数据范围】
Subtask \text{Subtask} Subtask 分值 特殊性质 0 0 0 0 0 0 样例 1 1 1 11 11 11 n = 2 n=2 n=2 2 2 2 22 22 22 n , s ≤ 100 n,s\le100 n,s≤100 3 3 3 9 9 9 n , s ≤ 250 n,s\le250 n,s≤250 4 4 4 33 33 33 n , s ≤ 500 n,s\le500 n,s≤500 5 5 5 25 25 25 无 对于 100 % 100\% 100% 的数据, 1 ≤ n , s ≤ 1000 , 1 ≤ a i , j ≤ 1 0 9 1\le n,s \le 1000,1\le a_{i,j} \le 10^9 1≤n,s≤1000,1≤ai,j≤109。
1. 读题
这道题其实和洛谷 P1638 逛画展很像。题目大意:一所学校有 n n n 个班级,每个班级有 s s s 人。现在要在每个班里选出一个人进行拍照。为了照片的美观性,要使选出的这 n n n 个人中最高的人与最矮的人的身高差距最小,请你求出这个最小值。
2. 解题思路
用双指针滑动窗口的方法解题,如果班级种类不够了,就把 r r r指针右移,然后判断这个人是不是新的班级的人,如果是,计数器++。等班级种类足够的时候,我们就把多余的人去掉,也就是说,把当前窗口中同一个班级的同学去除。删完人以后,再看看班级种类数是不是正好为 n。如果是的话,就尝试更新答案。
3.代码片段分析
- 数据定义
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
struct st{
int c,h;
//c:班级(class)。
//h:身高(heit)。
}a[N];
int v[N];//当前窗口内班级i的学生数量。
int n,s,cnt;//输入变量。
//cnt:总学生数。
//(其他变量与题目描述一致)。
int l=1,r=1,num=1,ans=1e9;//滑动窗口变量。
//l:左指针。
//r:右指针。
//num:当前窗口内不同班级的数量。
//ans:记录答案,因为后面要打擂台,所以要开大点。
bool cmp(st x,st y){//排序辅助,按身高排序。
return x.h<y.h;
}
- 数据读入
cin>>n>>s;
for(int i=1;i<=n;i++)
for(int j=1;j<=s;j++){
int x;
cin>>x;
a[++cnt]=st{i,x};//变量i遍历的是班级,把i(班级)和x(身高)存入数组。
}
- 双指针操作(核心部分)
sort(a+1,a+cnt+1,cmp);//按身高排序。
v[a[1].c]=1;//初始化
while(l<=r&&r<=cnt){//双指针(滑动窗口)。
//l<r:左指针不超过右指针。
//r<cnt:右指针不超过总学生数。
while(num<n&&r<cnt){//窗口班级个数不超过班级总数。
v[a[++r].c]++;//累加
if(v[a[r].c]==1) num++;//如果a[r].c只第有一个,那就视为多了一个班级的学生。
}
while(v[a[l].c]>1) v[a[l++].c]--;//如果有多余班级的人,删掉
ans=min(ans,a[r].h-a[l].h);//如果正好满足,更新答案,打擂台。
v[a[l].c]--;
if(v[a[l].c]==0) num--;//把当前这个人删掉,
//由于他没有被上面的 while 删掉,因此他是这个班级的最后一员
//所以班级种类减少了
}
cout<<ans;
return 0;
4. AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
struct st{
int c,h;
}a[N];
int v[N];
int n,s,cnt;
int l=1,r=1,num=1,ans=0x3f3f3f3f;
bool cmp(st x,st y){
return x.h<y.h;
}
int main(){
cin>>n>>s;
for(int i=1;i<=n;i++)
for(int j=1;j<=s;j++){
int x;
cin>>x;
a[++cnt]=st{i,x};
}
sort(a+1,a+cnt+1,cmp);
v[a[1].c]++;
while(l<=r&&r<=cnt){
while(num<n&&r<cnt){
v[a[++r].c]++;
if(v[a[r].c]==1) num++;
}
while(v[a[l].c]>1) v[a[l++].c]--;
if(num==n) ans=min(ans,a[r].h-a[l].h);
v[a[l++].c]--;
num--;
}
cout<<ans;
return 0;
}
如果对你有帮助,点个赞再走吧!谢谢!有什么问题请及时指出
764

被折叠的 条评论
为什么被折叠?



