第21次CCF计算机软件能力认证【期末预测之最佳阈值】

第21次CCF202012-2(Python求解)

无脑暴力求解居然得不到满分,现在的csp从第二题就得动脑筋了吗/(ㄒoㄒ)/~~

题目背景

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

题目描述

具体来说,顿顿评估了 𝑚 位同学上学期的安全指数,其中第 𝑖(1≤𝑖≤𝑚)位同学的安全指数为 𝑦𝑖,是一个 [0,108] 范围内的整数;同时,该同学上学期的挂科情况记作 𝑟𝑒𝑠𝑢𝑙𝑡𝑖∈0,1,其中 0 表示挂科、1 表示未挂科。

相应地,顿顿用 𝑝𝑟𝑒𝑑𝑖𝑐𝑡𝜃(𝑦) 表示根据阈值 𝜃 将安全指数 𝑦 转化为的具体预测结果。 如果 𝑝𝑟𝑒𝑑𝑖𝑐𝑡𝜃(𝑦𝑗) 与 𝑟𝑒𝑠𝑢𝑙𝑡𝑗 相同,则说明阈值为 𝜃 时顿顿对第 𝑗 位同学是否挂科预测正确;不同则说明预测错误。

最后,顿顿设计了如下公式来计算最佳阈值 𝜃∗

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

最佳阈值仅在 𝑦𝑖 中选取,即与某位同学的安全指数相同;
按照该阈值对这 𝑚 位同学上学期的挂科情况进行预测,预测正确的次数最多(即准确率最高);
多个阈值均可以达到最高准确率时,选取其中最大的。
输入格式
从标准输入读入数据。

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

接下来输入 𝑚 行,其中第 𝑖(1≤𝑖≤𝑚)行包括用空格分隔的两个整数 𝑦𝑖 和 𝑟𝑒𝑠𝑢𝑙𝑡𝑖,含义如上文所述。

输出格式
输出到标准输出。

输出一个整数,表示最佳阈值 𝜃∗。
样例1输入

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

样例1输出

3

子任务

70 的测试数据保证 𝑚≤200;

全部的测试数据保证 2≤𝑚≤105。

问题求解

一、采用嵌套循环暴力求解

大家都会,直接上代码

m = int(input())
data = []
for i in range(m):
    y, res = map(int, input().split())
    temp = []
    temp.append(y)
    temp.append(res)
    data.append(temp)

best = data[0][0]
bestnum = 0
for data_item in data:
    temp = data_item[0]
    rightnum = 0
    for item in data:
        if item[0] < temp and item[1] == 0:
            rightnum += 1
        if item[0] >= temp and item[1] == 1:
            rightnum += 1
    
    if rightnum > bestnum:
        best = temp
        bestnum = rightnum
    if rightnum == bestnum and temp > best:
        best = temp

print(best)

运行结果:
在这里插入图片描述
超时,子任务中表面,m最大达10的5次方,嵌套循环必然耗费时间

二、改进算法

1.第一步

首先将输入的y和其对应result值进行排序。先将y和result放在一个列表中,再将这个列表其放在另一个列表中,对列表使用.sort()排序,得到以y值递增排列的列表,y值相同时,result=0的在前面,result=1的在后面。

[[0, 0],
 [1, 0], 
 [1, 1], 
 [3, 1], 
 [5, 1], 
 [7, 1]]
2.第二步

观察发现,每个y预测正确的次数等于大于他的y值中对应result=1的个数 + 小于它的y值中对应result=0的个数 + 考虑y值相等的情况。

3.第三步

对列表进行扩充,在y,result值后添加到它为止(不包括自己)0的个数以及到它为止(包括自己)1的个数。补充后如下

[0, 0, 0, 0] # 第一个元素前面没有数据,都是0
[1, 0, 1, 0] # 这个1前面有1个比它小的y对应result=0, 故填充1,0
[1, 1, 2, 1] # 这个1前面有2个比它小的y(包括等于)对应result=0,有1个比它大的(包括自己)y对应result=1, 故填充2,1
[3, 1, 2, 2] # 3前面有2个0,两个1(包括自己的1)
[5, 1, 2, 3]
[7, 1, 2, 4]
4.第四步

处理重复项,在计算中,可以注意到,计算1的个数的时候,可以算上和y值相同的项,但计算0的个数的时候,不算y值相同的项。

5.代码实现
"""首先将输入的y和其对应result值进行排序。先将y和result放在一个列表中,
	再将这个列表其放在另一个列表中,对列表使用.sort()排序,
	得到以y值递增排列的列表,y值相同时,result=0的在前面,result=1的在后面。"""
m = int(input())
data = []
for i in range(m):
    y, res = map(int, input().split())
    temp = []
    temp.append(y)
    temp.append(res)
    data.append(temp)
data.sort()

"""对列表进行扩充,在y,result值后添加到它为止(不包括自己)0的个数
    以及到它为止(包括自己)1的个数。补充后如下"""
num0 = 0
num1 = 0
for item in data:
    if item[1] == 0:
        item.append(num0)
        item.append(num1)
        num0 += 1
    if item[1] == 1:
        num1 += 1
        item.append(num0)
        item.append(num1)

best = data[0][0]
bestnum = 0
Max1 = data[m-1][3]

""".处理重复项,在计算中,可以注意到,计算1的个数的时候,
    可以算上和y值相同的项,但计算0的个数的时候,不算y值相同的项。"""
for i in range(1,m):
    if data[i][0] == data[i-1][0]:
        data[i][2] = data[i-1][2]


"""计算最佳阈值"""
for i in range(m):
    rightnum = 0
    
    if i == 0:
        if data[i][1] == 1:
            rightnum = Max1 - data[i][3] + 1
        else:
            rightnum = Max1 - data[i][3]
        # print("{0}的正确次数为{1}".format(data[i][0], rightnum))
        continue
    
    if data[i][1] == 1:
        rightnum = data[i][2] + Max1 - data[i][3] + 1
    else:
        rightnum = data[i][2] + Max1 - data[i][3]
    # print("{0}的正确次数为{1}".format(data[i][0], rightnum))
    
    if rightnum > bestnum:
        best = data[i][0]
        bestnum = rightnum
    if rightnum == bestnum and data[i][0] > best:
        best = data[i][0]
# 打印结果
print(best)

最终结果
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值