CSP简单题题解 202012-2
本次题解为 202012-13 第21次CCF计算机软件能力认证
题目链接🔗 202012-2 期末预测之最佳阈值 ,
题解在网络上发布:方便大家获取代码,自己上手删改试试看
对于此处的第二题来说,我们在初次尝试了暴力解的过程中可以隐约感感觉到必须要用前缀和才行,但具体怎么用,如何判段把哪一部分作为前缀和,这个题目给出了一定的思路。
202012-2 期末预测之最佳阈值
70分方法:暴力解 O ( n 2 ) O(n^2) O(n2)
- 从例子中寻找灵感:“完成作业的最直观思路”,如下图所示,题目中的样例解释其实也在引导你
- 注意 θ \theta θ的选择,必须是在y中曾经出现过的,并不是从最小开始迭代到最大
#include<iostream>
using namespace std;
int MAX = 100000;
int main()
{
int y[MAX];
int result[MAX];
int seta_array[MAX];
int differ_seta = 0; //决定外层循环的边界
int max_right = 0;
int best_seta = 0;
int m,j;
cin>>m;
for(int i = 0; i<m; i++){
cin>>y[i]>>result[i];
for( j = 0; j<differ_seta; j++){
if(seta_array[j] == y[i])break;
}
if(j == differ_seta)seta_array[differ_seta++] = y[i];
// 找出有多少不一样的y
}
if(differ_seta == 0)best_seta = y[0];
for(int i=0; i<differ_seta; i++){ //外层循环 - 以seta来增值:最坏的情况seta有m个
int seta = seta_array[i];
int right = 0;
for(int j = 0; j<m; j++){ //内层 循环 m次
if(y[j] >= seta && result[j] == 1)right++;
else if(y[j] < seta && result[j] == 0)right++;
}
//保证取尽量大的seta
if(right > max_right){
max_right = right;
best_seta = seta;
}
else if(right == max_right && seta > best_seta){
best_seta = seta;
}
}
cout<<best_seta<<endl;
return 0;
}
100分解法:利用 前缀和 与 排序 O ( n l o g n ) O(n logn) O(nlogn)
- 启示:务必要熟悉结构体的定义、会运用Cpp内置的排序函数比如sort,以免自己手写
- 关于动态分配空间的大小,仍然不严格;写死了也可以过,但动态分配并不难,何乐不为
- 体会前缀和的含义 — — 隐含了本来 还需要
O
(
n
)
O(n)
O(n)来遍历的信息,使得可以快速查询到。
- “前缀”的含义不在于字面上:有可能是“后缀”,比如 此中的ones
- 如何去分析哪里需要这个 “和” — — 想去遍历哪里
- ones只会减值,zeros总会增值;因为总是想找当前位置之前的“0”,之后的“1”
#include<iostream>
#include<algorithm>
using namespace std;
struct student
{
int y;
int result;
};
bool cmp(student s1, student s2)
{
if(s1.y != s2.y) return s1.y < s2.y; //Qsort是升序, 先按y排
else return s1.result < s2.result; //相同时,0 在 1 前
}
int main()
{
int m;
int ones = 0,zeros = 0;
int best_seta = 0;
int seta = 0;
cin>>m;
student *stu = NULL;
stu = new student[m];
for(int i =0; i<m; i++){
cin>>stu[i].y>>stu[i].result;
if(stu[i].result == 1)ones++; //统计出 所有result=1次数,所有result1=1的同学中的最小y就是最佳的seta
}
sort(stu, stu+m, cmp); //快排, 时间复杂度O(nlogn)
int max_right = 0;
int right = 0;
//注意考虑 5 0/5 0/5 1/5 1/6 1 ---- y为5的所有情况 不要看作一个整体
for(int i=1; i<m; i++){
//总是在与前一个比 (前缀和),当前元素的情况,含在了ones中
if (stu[i-1].result == 0) zeros++; //zeros只会不断增值
else if (stu[i-1].result == 1)ones--; //ones只会减值
right = zeros + ones;
//规避 y相同的情况:best_seta没有变化,但前缀和变化了
if(stu[i-1].y != stu[i].y && right >= max_right){
best_seta = stu[i].y;
max_right = right;
}
}
cout<<best_seta<<endl;
return 0;
}