CCF认证期末预测之最佳阈值

期末预测之最佳阈值

题目描述

具体来说,顿顿评估了 m m m位同学上学期的安全指数,其中第 i ( 1 ≤ i ≤ m ) i(1\le i\le m) i(1im)位同学的安全指数为 y i y_i yi,是一个 [ 0 , 1 0 8 ] [0,10^8] [0,108]范围内的整数;同时,该同学上学期的挂科情况记作 r e s u l t i ∈ { 0 , 1 } result_i\in \{0,1\} resulti{0,1},其中 0 0 0表示挂科、 1 1 1表示未挂科。

相应地,顿顿用 p r e d i c t θ ( y ) predict_{\theta}(y) predictθ(y)表示根据阈值 θ \theta θ将安全指数 y y y转化为的具体预测结果。 如果 p r e d i c t θ ( y j ) predict_{\theta}(y_j) predictθ(yj) r e s u l t j result_j resultj相同,则说明阈值为 θ \theta θ时顿顿对第 j j j位同学是否挂科预测正确;不同则说明预测错误。

p r e d i c t θ ( y ) = { 0 ( y < θ ) 1 ( y ≥ θ ) predict_{\theta}(y)=\begin{cases}0&(y\lt \theta)\\1&(y\ge \theta)\end{cases} predictθ(y)={01(y<θ)(yθ)
最后,顿顿设计了如下公式来计算最佳阈值 θ ∗ \theta^* θ
θ ∗ = max ⁡ arg max ⁡ θ ∈ y i ∑ j = 1 m ( p r e d i c t θ ( y j ) = = r e s u l t j ) \theta^*=\max \argmax\limits_{\theta\in y_i}\sum\limits_{j=1}^{m}(predict_{\theta}(y_j)==result_j) θ=maxθyiargmaxj=1m(predictθ(yj)==resultj)

该公式亦可等价地表述为如下规则:

最佳阈值仅在 y i y_i yi中选取,即与某位同学的安全指数相同;
按照该阈值对这 m m m位同学上学期的挂科情况进行预测,预测正确的次数最多(即准确率最高);
多个阈值均可以达到最高准确率时,选取其中最大的。

输入格式

从标准输入读入数据。

输入的第一行包含一个正整数 m m m

接下来输入 m m m行,其中第 i ( 1 ≤ i ≤ m ) i(1\le i\le m) i(1im)行包括用空格分隔的两个整数 y i y_i yi r e s u l t i result_i resulti,含义如上文所述。

输出格式

输出到标准输出。

输出一个整数,表示最佳阈值 θ ∗ \theta^* θ

样例1输入

6
0 0
1 0
1 1
3 1
5 1
7 1

样例1输出

3

样例1解释

按照规则一,最佳阈值的选取范围为 0 , 1 , 3 , 5 , 7 0,1,3,5,7 0,1,3,5,7

θ = 0 \theta=0 θ=0时,预测正确次数为 4 4 4

θ = 1 \theta=1 θ=1时,预测正确次数为 5 5 5

θ = 3 \theta=3 θ=3时,预测正确次数为 5 5 5

θ = 5 \theta=5 θ=5时,预测正确次数为 4 4 4

θ = 7 \theta=7 θ=7时,预测正确次数为 3 3 3

阈值选取为 1 1 1 1 1 1时,预测准确率最高; 所以按照规则二,最佳阈值的选取范围缩小为 1 , 3 1,3 1,3

依规则三, θ ∗ = max ⁡ { 1 , 3 } = 3 \theta^*=\max\{1,3\}=3 θ=max{1,3}=3

样例2输入

8
5 1
5 0
5 0
2 1
3 0
4 0
100000000 1

样例2输出

100000000

子任务

70 % 70\% 70%的测试数据保证 m ≤ 200 m\le 200 m200

全部的测试数据保证 2 ≤ m ≤ 1 0 5 2\le m\le 10^5 2m105

思路

注意到此题可以化简思路:对于数组 a a a中的每个值 k e y key key,求出在 r e s u l t ∈ { 0 , 1 } result\in \{0, 1\} result{0,1}时, y ≥ k e y y\ge key ykey y < k e y y\lt key y<key的元素个数。

暴力法:

k e y key key只有一个值时,很容易查找,只需要遍历一遍数组即可解决,所以暴力法的思路是对每个 k e y key key进行枚举。
两层循环,外循环枚举每一个值,内循环遍历每一个数据判断是否满足 p r e d i c t = = r e s u l t predict==result predict==result,进行了两层循环,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
数据量到了 1 0 5 10^5 105级别, O ( n 2 ) O(n^2) O(n2)显然时间会爆掉。

优化算法

一般 1 0 5 10^5 105数据量都会用 O ( n log ⁡ n ) O(n\log n) O(nlogn)的时间复杂度,所以考虑排序算法。
排序后有两个问题:

  1. r e s u l t = 0 result=0 result=0 r e s u l t = 1 result=1 result=1处理方式不同,所以将两种情况单独存储,将 r e s u l t = 0 result=0 result=0 r e s u l t = 1 result=1 result=1的数据用两个 v e c t o r vector vector分开存储。
  2. 排序完的操作:采用二分查找即可

排序的时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn),对每个元素枚举后进行一次二叉查找为 O ( n log ⁡ n ) O(n\log n) O(nlogn),则时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include <bits/stdc++.h>
using namespace std;
vector<int> a[2];
int ans, m, x, y, val;
int main() {
    scanf("%d", &m);
    for(int i = 1; i <= m; i++) scanf("%d %d", &x, &y), a[y].push_back(x);
    for(int i = 0; i < 2; i++) sort(a[i].begin(), a[i].end());
    for(int i = 0; i < 2; i++) {
        int len = a[i].size();
        for(int j = 0; j < len; j++) {
            int s = (lower_bound(a[0].begin(), a[0].end(), a[i][j]) - a[0].begin()) + (a[1].size() - (lower_bound(a[1].begin(), a[1].end(), a[i][j]) - a[1].begin()));
            if(s > val || (s == val && a[i][j] > ans)) {
                val = s;
                ans = a[i][j];
            }
        }
    }
    printf("%d", ans);
    return 0;
}

前缀和做法

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
unordered_map<int, bool> ext;
pair<int, int> a[maxn];
int n, x, y, s[maxn], ans, Max;
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d %d", &x, &y), a[i] = make_pair(x, y);
    sort(a + 1, a + 1 + n);
    for(int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i].second;
    for(int i = 1; i <= n; i++) {
        if(ext[x = a[i].first]) continue;
        ext[x] = true;
        int val = s[n] + i - 1 - 2 * s[i - 1];
        if(val >= Max) {
            Max = val;
            ans = x;
        }
    }
    printf("%d", ans);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值