因此,我目前正在为比赛做准备(澳大利亚信息学奥林匹克竞赛),在training hub中,AIO 2018中间件存在一个问题,称为Castle Cavalry。我完成了:
input = open("cavalryin.txt").read()
output = open("cavalryout.txt", "w")
squad = input.split()
total = squad[0]
squad.remove(squad[0])
squad_sizes = squad.copy()
squad_sizes = list(set(squad))
yn = []
for i in range(len(squad_sizes)):
n = squad.count(squad_sizes[i])
if int(squad_sizes[i]) == 1 and int(n) == int(total):
yn.append(1)
elif int(n) == int(squad_sizes[i]):
yn.append(1)
elif int(n) != int(squad_sizes[i]):
yn.append(2)
ynn = list(set(yn))
if len(ynn) == 1 and int(ynn[0]) == 1:
output.write("YES")
else:
output.write("NO")
output.close()
我提交了此代码,但没有通过,因为它太慢了,只有1.952秒。时间限制为1.000秒。我不确定如何缩短它,对我来说看起来还不错。请记住,我仍在学习,而我只是一个业余爱好者。我从今年才开始编写代码,因此,如果答案很明显,很抱歉浪费您的时间?。
谢谢您的帮助!
答案 0 :(得分:1)
一个性能问题是在同一实体上或已经int()
上反复调用int
:
if int(squad_sizes[i]) == 1 and int(n) == int(total):
elif int(n) == int(squad_sizes[i]):
elif int(n) != int(squad_sizes[i]):
if len(ynn) == 1 and int(ynn[0]) == 1:
但是真正的问题是您的代码无法正常工作。并且使其更快不会改变这一点。考虑输入:
4
2
2
2
2
您的代码将输出"NO"
(缺少换行符),尽管它是有效的配置。这是由于您在代码的早期使用set()
折叠了小队的大小。您已经舍弃了重要信息,而实际上只是测试数据的一个子集。为了进行比较,这是我认为可以正确处理输入的完整重写:
with open("cavalryin.txt") as input_file:
string = input_file.read()
total, *squad_sizes = map(int, string.split())
success = True
while squad_sizes:
squad_size = squad_sizes.pop()
for _ in range(1, squad_size):
try:
squad_sizes.remove(squad_size) # eliminate n - 1 others like me
except ValueError:
success = False
break
else: # no break
continue
break
with open("cavalryout.txt", "w") as output_file:
print("YES" if success else "NO", file=output_file)
请注意,我很早就将所有输入都转换为int
,因此不必再次考虑该问题。我不知道这是否会满足AIO的时间限制。
答案 1 :(得分:0)
我可以看到其中一些可能效率不高的东西,但是优化代码的最佳方法是对其进行概要分析:使用概要分析器和示例数据运行它。
您可以轻松地浪费时间尝试加速不需要的零件,而不会产生太大的影响。阅读标准库中的cProfile
模块,以了解如何执行此操作并解释输出。概要分析教程可能太长了,无法在此处复制。
我的建议,没有描述,
squad.remove(squad[0])
删除大列表的开头很慢,因为列表的其余部分必须在向下移动时复制。 (删除列表的末尾速度更快,因为列表通常由始终过度分配(比元素更多的插槽)的数组作为后盾,以使.append()更快,因此只需减少长度即可保持相同数组。
最好将其设置为虚拟值,然后在将其转换为集合时将其删除(集合由哈希表支持,因此删除速度很快),例如
dummy = object()
squad[0] = dummy # len() didn't change. No shifting required.
...
squad_sizes = set(squad)
squad_sizes.remove(dummy) # Fast lookup by hash code.
由于我们知道这些都是字符串,因此您可以仅使用None
而不是虚拟对象,但是即使您的列表中可能包含None
,上述技术也可以使用。
squad_sizes = squad.copy()
此行不是必需的;它只是在做额外的工作。 set()
已经进行了浅拷贝。
n = squad.count(squad_sizes[i])
这行可能是真正的瓶颈。它实际上是一个循环中的一个循环,因此它基本上必须扫描整个列表以了解每个外部循环。考虑将collections.Counter
用于此任务。您可以在循环外生成一次计数表,然后只需查找每个字符串的数字即可。
如果这样做,也可以完全避免生成集合。只需使用Counter
对象的键进行设置即可。
与性能无关的另一点。在不需要索引时使用[i]
这样的索引是不切实际的。 for循环可以从迭代器中获取元素,然后一步将其分配给变量:
from collections import Counter
...
count_table = Counter(squad)
for squad_size, n in count_table.items():
...
答案 2 :(得分:0)
您可以收集字典中每个骑士的所有出现的首选编号。 然后测试具有给定首选号码的骑士人数是否可以被该人数整除。
with open('cavalryin.txt', 'r') as f:
lines = f.readlines()
# convert to int
list_int = [int(a) for a in lines]
#initialise counting dictionary: key: preferred number, item: empty list to collect all knights with preferred number.
collect_dict = {a:[] for a in range(1,1+max(list_int[1:]))}
print(collect_dict)
# loop though list, ignoring first entry.
for a in list_int[1:]:
collect_dict[a].append(a)
# initialise output
out='YES'
for key, item in collect_dict.items():
# check number of items with preference for number is divisilbe
# by that number
if item: # if list has entries:
if (len(item) % key) > 0:
out='NO'
break
with open('cavalryout.txt', 'w') as f:
f.write(out)