第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)
最终结果