2021/4/11更新:第22次认证第二题也是用前缀和xdm!!!
题目传送门: 期末预测之最佳阈值
题目分析
题目大意是,将安全指数yi作为阈值(yi有几种情况阈值就有几种情况),然后将所有安全指数yi依次与选定好的阈值进行比较:
①如果安全指数yi大于等于阈值时,挂科情况resulti=1代表预测正确;
②如果安全指数yi小于阈值时,挂科情况resulti=0代表预测正确。
两种情况的和作为总的预测正确数,选取预测正确数最高时候的阈值作为答案,如果阈值有多个则取最大的。
那么解题思路一句话概括就是先对数据按照安全指数yi进行升序排序,然后求出比yi小的0的个数,比yi大的1的个数。
以样例1为例,当选3位阈值时,预测正确次数是2+3=5
因为题目数据规模m为1e5,如果用暴力两层for循环是会超时的,只能通过70%的数据,不能拿满分。那么这道题真正要考查的知识点是前缀和,通过前缀和大大降低时间复杂度,那么首先我们先简单认识一下前缀和。
前缀和
前缀和是一种重要的预处理,能大大降低查询的时间复杂度。
最简单的一道题就是给定 n 个数和 m 次询问,每次询问一段区间的和。求一个 O(n + m) 的做法。
用 O(n) 前缀和预处理,O(m) 询问。
主要代码
for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i]; //O(n)
while(m--) //O(m)
{
int L, R; scanf("%d%d", &L, &R);
printf("%d\n", sum[R] - sum[L - 1]);
}
那么该题我们需要用到前缀和求出resulti数组的前缀和,以方便我们之后循环安全指数yi时快速求出1和0的个数。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,Max=0,res;
int sum[N]={0};
set<int>st;
pair<int,int>pr[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
pr[i]=make_pair(a,b);
}
sort(pr+1,pr+n+1);//1.先排序
for(int i=1;i<=n;i++)
sum[i] =sum[i-1]+ pr[i].second;//2.求挂科情况前缀和
for(int i=1;i<=n;i++)
{
int a=pr[i].first;//选取阈值
if(st.count(a)) continue;//set去重
st.insert(a);
int yuce1 = sum[n]-sum[i-1];//大于等于阈值时,应统计预测结果中为1的个数
int yuce0 = i-1-sum[i-1];//小与阈值时,应统计预测结果中为0的个数
int yuce = yuce1+ yuce0;//合计预测正确次数
if(yuce >= Max) {
Max=yuce;
res=a;
}
}
cout<<res;
return 0;
}
代码额外注释:
1.如果对C++的结构模板pair不熟悉,完全可以用结构体代替,其实pair实质上就是一个结构体。
2.因为安全指数yi值有相同的情况,但是阈值只需要用一次,所以代码23行要用到set集合的count方法判断是否已经出现过,如果没有则加入集合,否则进行下一次循环。
3.代码25行就是用前缀和计算大小,yuce1有多大就有多少个1。
4.代码26行,sum[i-1]是从result1到阈值前resulti的和即1的个数,那么再用i-1减去即是0的个数。
大家多学习一些C++的STL大有好处,代码会非常简洁明了。