简体   繁体   English

如何将O(N * M)优化为O(n ** 2)?

[英]How to optimize an O(N*M) to be O(n**2)?

I am trying to solve USACO's Milking Cows problem. 我正在尝试解决USACO的奶牛问题。 The problem statement is here: https://train.usaco.org/usacoprob2?S=milk2&a=n3lMlotUxJ1 问题陈述在这里: https : //train.usaco.org/usacoprob2?S=milk2&a=n3lMlotUxJ1

Given a series of intervals in the form of a 2d array, I have to find the longest interval and the longest interval in which no milking was occurring. 给定一系列二维数组形式的间隔,我必须找到最长的间隔和没有挤奶的最长间隔。

Ex. 防爆。 Given the array [[500,1200],[200,900],[100,1200]] , the longest interval would be 1100 as there is continuous milking and the longest interval without milking would be 0 as there are no rest periods. 给定数组[[500,1200],[200,900],[100,1200]] ,由于连续挤奶,最长间隔为1100,没有挤奶的最长间隔为0,因为没有休息时间。

I have tried looking at whether utilizing a dictionary would decrease run times but I haven't had much success. 我曾尝试查看使用字典是否会减少运行时间,但并没有取得太大的成功。

f = open('milk2.in', 'r')
w = open('milk2.out', 'w')

#getting the input
farmers = int(f.readline().strip())
schedule = []
for i in range(farmers):
    schedule.append(f.readline().strip().split())


#schedule = data
minvalue = 0
maxvalue = 0

#getting the minimums and maximums of the data 
for time in range(farmers):
    schedule[time][0] = int(schedule[time][0])
    schedule[time][1] = int(schedule[time][1])
    if (minvalue == 0):
        minvalue = schedule[time][0]
    if (maxvalue == 0):
        maxvalue = schedule[time][1]
    minvalue = min(schedule[time][0], minvalue)
    maxvalue = max(schedule[time][1], maxvalue)

filled_thistime = 0
filled_max = 0

empty_max = 0
empty_thistime = 0

#goes through all the possible items in between the minimum and the maximum
for point in range(minvalue, maxvalue):
    isfilled = False
    #goes through all the data for each point value in order to find the best values
    for check in range(farmers):
        if point >= schedule[check][0] and point < schedule[check][1]:
            filled_thistime += 1
            empty_thistime = 0
            isfilled = True
            break
    if isfilled == False:
        filled_thistime = 0
        empty_thistime += 1
    if (filled_max < filled_thistime) : 
        filled_max = filled_thistime 
    if (empty_max < empty_thistime) : 
        empty_max = empty_thistime 
print(filled_max)
print(empty_max)
if (filled_max < filled_thistime):
    filled_max = filled_thistime

w.write(str(filled_max) + " " + str(empty_max) + "\n")
f.close()
w.close()

The program works fine, but I need to decrease the time it takes to run. 该程序工作正常,但我需要减少运行时间。

As stated in the comments, if the input is sorted, the complexity could be O(n), if that's not the case we need to sort it first and the complexity is O(nlog n): 如评论中所述,如果对输入进行排序,则复杂度可能为O(n),如果不是这种情况,我们需要首先对其进行排序,并且复杂度为O(nlog n):

lst = [ [300,1000],
[700,1200],
[1500,2100] ]

from itertools import groupby

longest_milking = 0
longest_idle = 0

l = sorted(lst, key=lambda k: k[0])

for v, g in groupby(zip(l[::1], l[1::1]), lambda k: k[1][0] <= k[0][1]):
    l = [*g][0]
    if v:
        mn, mx = min(i[0] for i in l), max(i[1] for i in l)
        if mx-mn > longest_milking:
            longest_milking = mx-mn
    else:
        mx = max((i2[0] - i1[1] for i1, i2 in zip(l[::1], l[1::1])))
        if mx > longest_idle:
            longest_idle = mx

# corner case, N=1 (only one interval)
if len(lst) == 1:
    longest_milking = lst[0][1] - lst[0][0]

print(longest_milking)
print(longest_idle)

Prints: 打印:

900
300

For input: 输入:

lst = [ [500,1200],
        [200,900],
        [100,1200] ]

Prints: 打印:

1100
0

A less pretty but more efficient approach would be to solve this like a free list, though it is a bit more tricky since the ranges can overlap. 一种不那么漂亮但更有效的方法是像一个自由列表一样解决此问题,尽管由于范围可能会重叠,所以它有些棘手。 This method only requires looping through the input list a single time. 此方法仅需要循环遍历输入列表一次。

def insert(start, end):
    for existing in times:
        existing_start, existing_end = existing
        # New time is a subset of existing time
        if start >= existing_start and end <= existing_end:
            return
        # New time ends during existing time
        elif end >= existing_start and end <= existing_end:
            times.remove(existing)
            return insert(start, existing_end)
        # New time starts during existing time
        elif start >= existing_start and start <= existing_end:
            # existing[1] = max(existing_end, end)
            times.remove(existing)
            return insert(existing_start, end)
        # New time is superset of existing time
        elif start <= existing_start and end >= existing_end:
            times.remove(existing)
            return insert(start, end)
    times.append([start, end])

data = [
    [500,1200],
    [200,900],
    [100,1200] 
]

times = [data[0]]
for start, end in data[1:]:
    insert(start, end)

longest_milk = 0
longest_gap = 0
for i, time in enumerate(times):
    duration = time[1] - time[0]
    if duration > longest_milk:
        longest_milk = duration
    if i != len(times) - 1 and times[i+1][0] - times[i][1] > longest_gap:
        longes_gap = times[i+1][0] - times[i][1]

print(longest_milk, longest_gap)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM