洛谷 P10710 [NOISG 2024 Prelim] School Photo 题解

洛谷 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\) 个班,每个班有 \(s\) 名同学。第 \(i\) 个班中的第 \(j\) 名同学的身高是 \(a_{i,j}\)
现在 Zane 想从每个班上选出一名同学拍照,使得这 \(n\) 名同学中最高的同学和最低的同学的身高差最小。
请你输出这个最小值。

输入格式

第一行,两个整数 \(n,s\)
接下来 \(n\) 行,每行 \(s\) 个整数,表示 \(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}\),答案为 \(8-4=4\)

【数据范围】

\(\text{Subtask}\) 分值 特殊性质
\(0\) \(0\) 样例
\(1\) \(11\) \(n=2\)
\(2\) \(22\) \(n,s\le100\)
\(3\) \(9\) \(n,s\le250\)
\(4\) \(33\) \(n,s\le500\)
\(5\) \(25\)
对于 \(100\%\) 的数据,\(1\le n,s \le 1000,1\le a_{i,j} \le 10^9\)

1. 读题

这道题其实和洛谷 P1638 逛画展很像。题目大意:一所学校有 \(n\) 个班级,每个班级有 \(s\) 人。现在要在每个班里选出一个人进行拍照。为了照片的美观性,要使选出的这 \(n\) 个人中最高的人与最矮的人的身高差距最小,请你求出这个最小值。


2. 解题思路

用双指针滑动窗口的方法解题,如果班级种类不够了,就把\(r\)指针右移,然后判断这个人是不是新的班级的人,如果是,计数器++。等班级种类足够的时候,我们就把多余的人去掉,也就是说,把当前窗口中同一个班级的同学去除。删完人以后,再看看班级种类数是不是正好为 n。如果是的话,就尝试更新答案。


3.代码片段分析

  1. 数据定义
#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;
}
  1. 数据读入
	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(身高)存入数组。
		}
  1. 双指针操作(核心部分)
	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=1e9;

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]=1;
	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]--;
		ans=min(ans,a[r].h-a[l].h);
		v[a[l].c]--;
		if(v[a[l].c]==0) num--;
	}
	cout<<ans;
	return 0;
}

如果对你有帮助,点个赞再走吧!谢谢!有什么问题请及时指出

posted @ 2025-07-16 11:39  sunhy2012  阅读(30)  评论(0)    收藏  举报