ccf 202012-2 期末预测之最佳阈值 python满分

问题限制

试题编号202012-2
试题名称期末预测之最佳阈值
时间限制1.0s
内存限制512.0MB

试题背景
考虑到安全指数是一个较大范围内的整数、小菜很可能搞不清楚自己是否真的安全,顿顿决定设置一个阈值 ,以便将安全指数y转化为一个具体的预测结果——“会挂科”或“不会挂科”。
因为安全指数越高表明小菜同学挂科的可能性越低,所以当y≥θ时,顿顿会预测小菜这学期很安全、不会挂科;反之若y<θ,顿顿就会劝诫小菜:“你期末要挂科了,勿谓言之不预也。”
那么这个阈值该如何设定呢?顿顿准备从过往中寻找答案。

题目描述
在这里插入图片描述
输入格式
在这里插入图片描述
输出格式
在这里插入图片描述
样例1
在这里插入图片描述
样例2
在这里插入图片描述
子任务
在这里插入图片描述
解题思路
从子任务上看,如果想拿一百分,O(m^2)是绝对不行的,也就是说如果想双重循环就会超时,最好能将时间复杂度降低到O(m)或者O(mlogm),题目的意思大概就是找一个下标最大的阈值使低于该阈值的预测为0的同学的个数加上大于等于该阈值的预测为1的同学的个数的数量之和最大,说起来有点绕口,具体来说,首先将读入的数据按照第一关键字从小到大,再按照第二关键字从小到大的顺序拍个序,来确保每位同学的安全指数y是按照非递减的顺序排列的,这样的目的是为了统计result为0和1各自的个数,因为一旦选取某个同学的安全指数作为阈值,那么低于该阈值的所有预测结果都应该是0,大于等于该阈值的所有预测结果都应该是1,那么排序的目的就是为了找到这个转折点,统计这个转折点之前所有result为0的个数zero_num[i],以及这个转折点之后所有result为1的个数ones_num[i],这两个数组的计算很类似与以为前缀和,没什么好说的。
那么对于每个同学的安全指数作为阈值的预测正确次数的计算就可以简单的用zero_num[i-1]+total(1的总和可以代表1的个数)-ones_num[i],这样的话,可以直接将O(m^2)的时间复杂度降低到O(m),但是要注意,这个地方有个错误,就是统计该索引之前所有result为0的个数真的对吗?
打个比方,这样一组数据,[[1, 0], [2, 1], [3, 0], [4, 0], [5, 0], [5, 0], [5, 1], [100000000, 1]],按理说第6个5的预测正确结果应该是5,但是按照这个计算公式计算结果是6,这是因为这个公式把第5个5也作为正确预测结果了,当我们以某个安全指数作为阈值时,其实这个等于该安全系数的所有同学的预测结果我们都应该单独考虑的,所以不应该是zero_num[6-1],应该是zero_num[5-1],只需要用一个二分查找就可找到大于等于5的第一个数的下标,这样的话,时间复杂度只是O(mlogm),仍然可以过,那肯定有人会问,你只确定一个左边界,右边界不管了吗?实际上,我们之所以会用到左边界,是因为在统计之前的0的个数的时候需要排除这个数本身的影响,但是这个数之后要统计的是1的数量,即使后边再多几个[5,0]也是不影响ones_num的数值的,所以无需确定右边界。

代码实现

#1.先排序,按照第一值从小到大,第二值从小到大的顺序
#2.就算每个元素之前所有元素不包括自己、数值上等于自己的也不算的0的数量,以及之后所有元素1的数量,同样不包括自己
#将这两个数量加上本身是否判断正确,就是ans,
def lbinary_find(lst,target):
    l=0
    r=m-1
    while l<r:
        mid=l+r>>1
        if lst[mid][0]>=target:
            r=mid
        else:
            l=mid+1
    return l
m=int(input())
zero_num,ones_num=[0 for i in range(m)],[0 for i in range(m)]
lst=[]
tal=0
for i in range(m):
    y,result=map(int,input().split())
    lst.append([y,result])
    tal+=result
lst.sort(key=lambda x:(x[0],x[1]))
#因为要用到i-1,所以对下标为0单独处理一下
zero_num[0]=0 if lst[0][1]==1 else 1
ones_num[0]=0 if lst[0][1]==0 else 1
for i in range(1,m):
    if lst[i][1]==1:
        ones_num[i]=ones_num[i-1]+1
        zero_num[i]=zero_num[i-1]
    elif lst[i][1]==0:
        zero_num[i]=zero_num[i-1]+1
        ones_num[i]=ones_num[i-1]
num=tal
index=0
for i in range(1,m):
    l=lbinary_find(lst,lst[i][0])
    tmp=zero_num[l-1]+tal-ones_num[i]
    if lst[i][1]==1:
        tmp+=1
    if num<=tmp:
        num=tmp
        index=i
print(lst[index][0])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

so.far_away

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值