CCF-2020-12-2 期末预测之最佳阈值(低俗题)-你留的眼泪,我来帮你拭去~~~

写在前面

去年的12月如期进行了CCF的第21次认证,很多同学一直都对第二题有着疑惑,为什么只能拿70分呢???
很多同学哭着走出了考场,因为前两题拿170分意味着没有达到200分的毕业要求!(后面不好骗分了呀!)
到底是肿么回事呢?
今天,就让我们一起走进科学,探索一下CCF的魅力!

我们看看题目肿么说!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

初心,第一想法

如果学过算法,我们提高算法都会说高雅,优秀。
但如果提起CCF的前两题,大家一般都只会说庸俗
所以大家都用平庸的想法,暴力,去做前两题
没想到的是第二题,呵呵,我只能说有点低俗

低俗在前两题就要用一点点算法,前两题一般就是暴力的。

我们先来看看暴力的做法:
优雅的暴力,你一定要看一看:

  1. ==两个vector数组保存输入。==两个输入,成绩和是否挂科(0,1),如果挂科,压入vector::zero,否则压入vector::one
  2. 阈值一定是输入的成绩,我们用set来保存成绩,这样可以保存有序且不会重复,节约了一定的时间
  3. 我们在判断阈值的个数的时候,先将zero和one进行sort排序,这样,我们就可以在不满足条件的时候break出来再节约一点时间(比如,a挂科,b比a分数低,b一定挂科就不用再去查询了,是不是)
  4. ==ios::sync_with_stdio(false);==解除限制,输入加速,冲冲冲!

代码和运行结果如下

#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> zero,one;
set<int> s;
int best_y,best_num=0;
int cnt_num;
int main(){
	ios::sync_with_stdio(false); 
	int m;
	cin>>m;
	while(m--){
		int temp_y,temp_result;
		cin>>temp_y>>temp_result;
		if(temp_result==0) zero.push_back(temp_y);
		else one.push_back(temp_y);
		s.insert(temp_y);
	}
	sort(zero.begin(),zero.end());
	sort(one.begin(),one.end()); 
	for(set<int>::iterator it=s.begin();it!=s.end();it++){
		cnt_num=0;
		for(int i=0;i<zero.size();i++){
			if(zero[i]<*it) cnt_num++;
			else break;
		}
		for(int i=one.size()-1;i>=0;i--){
			if(one[i]>=*it) cnt_num++;
			else break;
		}
		if(cnt_num>=best_num){
			best_num=cnt_num;
			best_y=*it;
		}
	}
	cout<<best_y<<endl;
	return 0;
}

在这里插入图片描述

前缀和才是这个题目的真正考点

竟然不考优雅的暴力!
多么低俗的考法哦!
简直就是偷袭好吧!
仔细一想,前缀和还有点动态规划的感觉。
这样就不用遍历了,是不是,和前面精打细算的节约相比,这个是质一样的提升!

我们理解一下前缀和

不说算法,搞点大家都懂的等差数列
比如一个数列,他有前n项和吧,这个就是前缀和,我晕!
大家就懂了!
所以,思路如下,很清晰的

  1. 我们将输入的成绩进行排序
  2. 然后我们求出前缀和—这个成绩作为阈值后,比当前成绩低的有多少个人不挂科
  3. 挂科的人数是当前成绩的排名(i-1)减去当前成绩的前缀和,减去不挂科的,就等于挂科的人数
  4. 不挂科的人数就是所有的不挂科的人数-比当前成绩低一点的不挂科的人数(因为他们现在要挂了。。。)
  5. 然后进行比较看有没有超过最值就行了,超过就更新,因为成绩从小到大,而我们又要同等数量的最大值,所以跑到最后就行了
  6. 注意这里的set只是用来查重的,因为,我们要找到当前成绩的排名,所以没有办法像第一段代码中再set中循环。

代码和运行结果如下

#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
#include<cstring>
#define mm(a,b) memset(a,b,sizeof(a))
using namespace std;
struct node{
	int y;
	int result;
};
bool cmp(node a,node b){
	return a.y<b.y;
}
vector<node> v;
set<int> s;
node temp;
const int N=1e5+5;
int sum[N];
int best_y,best_num;
int main(){
	mm(sum,0); //也可以直接用 int sum[N]={0} 代替 
	int m;
	cin>>m;
	for(int i=0;i<m;i++){
		cin>>temp.y>>temp.result;	
		v.push_back(temp);	
	}
	sort(v.begin(),v.end(),cmp);
	// 关键来了,就是求挂科前缀和
	for(int i=1;i<=m;i++){
		sum[i]=sum[i-1]+v[i-1].result;
	}
	int one,zero;
	for(int i=1;i<=m;i++){
		temp.y=v[i-1].y;
		if(s.count(temp.y)) continue;
		s.insert(temp.y);
		one=sum[m]-sum[i-1];
		zero=i-1-sum[i-1];
		temp.result=one+zero;
		if(temp.result>=best_num){
			best_y=temp.y;
			best_num=temp.result;
		}
	}
	cout<<best_y<<endl;
	return 0;
}

在这里插入图片描述

哎,反正我今天是低俗了一把

这都是为了拭去你去年12月流下的泪水呀!

谢谢大家看我的博客!!!

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值