洛谷 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 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 84=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,s100
3 3 3 9 9 9 n , s ≤ 250 n,s\le250 n,s250
4 4 4 33 33 33 n , s ≤ 500 n,s\le500 n,s500
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 1n,s1000,1ai,j109

1. 读题

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


2. 解题思路

用双指针滑动窗口的方法解题,如果班级种类不够了,就把 r r 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=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;
}


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

本文章已经生成可运行项目
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

—海燕—

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值