CCF-2020-12-2
写在前面
去年的12月如期进行了CCF的第21次认证,很多同学一直都对第二题有着疑惑,为什么只能拿70分呢???
很多同学哭着走出了考场,因为前两题拿170分意味着没有达到200分的毕业要求!(后面不好骗分了呀!)
到底是肿么回事呢?
今天,就让我们一起走进科学,探索一下CCF的魅力!
我们看看题目肿么说!
初心,第一想法
如果学过算法,我们提高算法都会说高雅,优秀。
但如果提起CCF的前两题,大家一般都只会说庸俗
所以大家都用平庸的想法,暴力,去做前两题
没想到的是第二题,呵呵,我只能说有点低俗
低俗在前两题就要用一点点算法,前两题一般就是暴力的。
我们先来看看暴力的做法:
优雅的暴力,你一定要看一看:
- ==两个vector数组保存输入。==两个输入,成绩和是否挂科(0,1),如果挂科,压入vector::zero,否则压入vector::one
- 阈值一定是输入的成绩,我们用set来保存成绩,这样可以保存有序且不会重复,节约了一定的时间
- 我们在判断阈值的个数的时候,先将zero和one进行sort排序,这样,我们就可以在不满足条件的时候break出来再节约一点时间(比如,a挂科,b比a分数低,b一定挂科就不用再去查询了,是不是)
- ==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项和吧,这个就是前缀和,我晕!
大家就懂了!
所以,思路如下,很清晰的
- 我们将输入的成绩进行排序
- 然后我们求出前缀和—这个成绩作为阈值后,比当前成绩低的有多少个人不挂科
- 挂科的人数是当前成绩的排名(i-1)减去当前成绩的前缀和,减去不挂科的,就等于挂科的人数
- 不挂科的人数就是所有的不挂科的人数-比当前成绩低一点的不挂科的人数(因为他们现在要挂了。。。)
- 然后进行比较看有没有超过最值就行了,超过就更新,因为成绩从小到大,而我们又要同等数量的最大值,所以跑到最后就行了
- 注意这里的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;
}