O‘REILLY: Lightbulb Operating —— 考虑灯泡工作时长的多灯泡下计算指定时间范围内多组时间差之和(秒数)

CheckIO是一个通过闯关游戏学习编程的网站(Python和JavaScript)。通过解题开发新“岛屿”,同时,通过做任务获得Quest Points解锁会员题目。
文章内容:题目、我自己的思路和代码以及优秀代码,如果想看大神解题可以直接跳到“优秀代码”部分。
本题链接:https://py.checkio.org/en/mission/lightbulb-operating/

背景

这部分是关于灯泡的一系列任务,有助于了解流程以及对流程效果的评估。 在现实生活中,除了灯泡,也需要计算其他设备的效果,或者上班的工人以及他们的工资。

题目

这一任务是这个系列里的第五个任务。

既然你已经来到这一任务,那就意味着已经完成了该系列的前4个任务。 你的函数已经可以在日期组成的列表中适用于多个灯泡来确定房间是否亮着。 同时可以使用第二和第三个元素来定义要观察的时间段。

在这一任务中,添加了第四个参数 灯泡的工作时间。与之前的任务类似,如果没有该参数,则灯泡可以无限期工作。

操作时间参数作为 timedelta 对象传递,表示灯泡可以工作的时长。 灯泡没有散热装置,这意味着如果灯泡只能工作一个小时,那么它可以现在工作30分钟,然后再工作30分钟。 此后,它将自行关闭,并且不再响应按钮。

这一任务仍要计算房间点亮了多长时间。

在这里插入图片描述

输入: 给定四个参数,第一个是必选参数,后几个是可选参数。第一个参数是时间格式组成的列表(也可以是由时间和整数组成的元组),第二个和第三个参数是观察开始时间和观察结束时间,第四个参数是灯泡能工作的时长。

输出: 秒数(整数)

举个栗子:

sum_light([
    datetime(2015, 1, 12, 10, 0, 0),
    datetime(2015, 1, 12, 10, 0, 30),
    (datetime(2015, 1, 12, 10, 0, 30), 2),
    (datetime(2015, 1, 12, 10, 1, 0), 2),
], operating=timedelta(seconds=20)) == 40

sum_light([
    (datetime(2015, 1, 12, 10, 0, 10), 3),
    datetime(2015, 1, 12, 10, 0, 20),
    (datetime(2015, 1, 12, 10, 0, 30), 3),
    (datetime(2015, 1, 12, 10, 0, 30), 2),
    datetime(2015, 1, 12, 10, 0, 40),
    (datetime(2015, 1, 12, 10, 0, 50), 2),
    (datetime(2015, 1, 12, 10, 1, 20), 2),
    (datetime(2015, 1, 12, 10, 1, 40), 2),
], start_watching=datetime(2015, 1, 12, 10, 0, 20), operating=timedelta(seconds=100)) == 50

sum_light([
    (datetime(2015, 1, 12, 10, 0, 10), 3),
    datetime(2015, 1, 12, 10, 0, 20),
    (datetime(2015, 1, 12, 10, 0, 30), 3),
    (datetime(2015, 1, 12, 10, 0, 30), 2),
], 
start_watching=datetime(2015, 1, 12, 10, 0, 10),
end_watching=datetime(2015, 1, 12, 10, 0, 30),
operating=timedelta(seconds=5)) == 10

假设:

  • 列表中的时间已经按升序排序
  • 列表中元素唯一(结果应该大于0)
  • 列表中元素个数为偶数个(灯泡最后是关闭状态)
  • 最小日期为:1970-01-01;最大日期为:9999-12-31

题目框架

from datetime import datetime, timedelta
from typing import List, Optional, Union, Tuple

def sum_light(els: List[Union[datetime, Tuple[datetime, int]]],
        start_watching: Optional[datetime] = None,
        end_watching: Optional[datetime] = None,
        operating: Optional[timedelta] = None) -> int:
    """
        how long the light bulb has been turned on
    """
    return 0


if __name__ == '__main__':
    print("Example:")

    print(sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
    ],
    start_watching=datetime(2015, 1, 12, 10, 0, 10),
    end_watching=datetime(2015, 1, 12, 10, 0, 30),
    operating=timedelta(seconds=5)))

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        (datetime(2015, 1, 12, 10, 0, 0), 2),
        datetime(2015, 1, 12, 10, 0, 10),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ]) == 60

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        datetime(2015, 1, 12, 10, 0, 10),
        (datetime(2015, 1, 12, 11, 0, 0), 2),
        (datetime(2015, 1, 12, 11, 1, 0), 2),
    ]) == 70

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ]) == 30
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ]) == 40

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
        (datetime(2015, 1, 12, 10, 1, 0), 3),
        (datetime(2015, 1, 12, 10, 1, 20), 3),
    ]) == 60

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        (datetime(2015, 1, 12, 10, 0, 0), 2),
        datetime(2015, 1, 12, 10, 0, 10),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ], datetime(2015, 1, 12, 10, 0, 50)) == 10
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 30)) == 20
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 20)) == 30
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 10)) == 30
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 50)) == 0
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 30)) == 20
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 20)) == 30
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
        (datetime(2015, 1, 12, 10, 1, 20), 2),
        (datetime(2015, 1, 12, 10, 1, 40), 2),
    ], datetime(2015, 1, 12, 10, 0, 20)) == 50
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        (datetime(2015, 1, 12, 10, 0, 0), 2),
        datetime(2015, 1, 12, 10, 0, 10),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ], datetime(2015, 1, 12, 10, 0, 30), datetime(2015, 1, 12, 10, 1, 0)) == 30
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        (datetime(2015, 1, 12, 10, 0, 0), 2),
        datetime(2015, 1, 12, 10, 0, 10),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ], datetime(2015, 1, 12, 10, 0, 20), datetime(2015, 1, 12, 10, 1, 0)) == 40
    
    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        (datetime(2015, 1, 12, 10, 0, 0), 2),
        datetime(2015, 1, 12, 10, 0, 10),
    ], datetime(2015, 1, 12, 10, 0, 0), datetime(2015, 1, 12, 10, 0, 30)) == 30
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 0), datetime(2015, 1, 12, 10, 1, 0)) == 40
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 0), datetime(2015, 1, 12, 10, 0, 10)) == 0
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
    ], datetime(2015, 1, 12, 10, 0, 10), datetime(2015, 1, 12, 10, 0, 20)) == 10
    

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
    ], datetime(2015, 1, 12, 10, 0, 10), datetime(2015, 1, 12, 10, 0, 20)) == 10
    
    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
    ], datetime(2015, 1, 12, 10, 0, 10), datetime(2015, 1, 12, 10, 0, 30)) == 20    

    assert sum_light(els=[
        (datetime(2015, 1, 11, 0, 0, 0), 3),
        datetime(2015, 1, 12, 0, 0, 0),
        (datetime(2015, 1, 13, 0, 0, 0), 3),
        (datetime(2015, 1, 13, 0, 0, 0), 2),
        datetime(2015, 1, 14, 0, 0, 0),
        (datetime(2015, 1, 15, 0, 0, 0), 2),
    ], start_watching=datetime(2015, 1, 10, 0, 0, 0), end_watching=datetime(2015, 1, 16, 0, 0, 0)) == 345600

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        datetime(2015, 1, 12, 10, 0, 10),
    ], operating=timedelta(seconds=100)) == 10

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        datetime(2015, 1, 12, 10, 0, 10),
    ], operating=timedelta(seconds=5)) == 5

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        datetime(2015, 1, 12, 10, 0, 10),
        (datetime(2015, 1, 12, 10, 0, 0), 2),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ], operating=timedelta(seconds=100)) == 60

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        datetime(2015, 1, 12, 10, 0, 30),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ], operating=timedelta(seconds=100)) == 60

    assert sum_light([
        datetime(2015, 1, 12, 10, 0, 0),
        datetime(2015, 1, 12, 10, 0, 30),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        (datetime(2015, 1, 12, 10, 1, 0), 2),
    ], operating=timedelta(seconds=20)) == 40

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
        (datetime(2015, 1, 12, 10, 1, 0), 3),
        (datetime(2015, 1, 12, 10, 1, 20), 3),
    ], operating=timedelta(seconds=10)) == 30

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
        (datetime(2015, 1, 12, 10, 1, 20), 2),
        (datetime(2015, 1, 12, 10, 1, 40), 2),
    ], start_watching=datetime(2015, 1, 12, 10, 0, 20), operating=timedelta(seconds=100)) == 50

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
        datetime(2015, 1, 12, 10, 0, 40),
        (datetime(2015, 1, 12, 10, 0, 50), 2),
        (datetime(2015, 1, 12, 10, 1, 20), 2),
        (datetime(2015, 1, 12, 10, 1, 40), 2),
    ], start_watching=datetime(2015, 1, 12, 10, 0, 20), operating=timedelta(seconds=10)) == 20

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
    ], start_watching=datetime(2015, 1, 12, 10, 0, 10), end_watching=datetime(2015, 1, 12, 10, 0, 30),
    operating=timedelta(seconds=20)) == 20

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
    ], start_watching=datetime(2015, 1, 12, 10, 0, 10), end_watching=datetime(2015, 1, 12, 10, 0, 30),
    operating=timedelta(seconds=10)) == 20

    assert sum_light([
        (datetime(2015, 1, 12, 10, 0, 10), 3),
        datetime(2015, 1, 12, 10, 0, 20),
        (datetime(2015, 1, 12, 10, 0, 30), 3),
        (datetime(2015, 1, 12, 10, 0, 30), 2),
    ], start_watching=datetime(2015, 1, 12, 10, 0, 10), end_watching=datetime(2015, 1, 12, 10, 0, 30),
    operating=timedelta(seconds=5)) == 10

    print("The forth mission in series is completed? Click 'Check' to earn cool rewards!")

难度: Simple+

思路及代码

思路

这个的题解虽然是用了上一道题的第二个优秀代码的思路,但是我做出来依然很麻烦 😦

在上一道题的第二个优秀代码的基础上,考虑在每一个控制按钮的时间点计算时间段长度,并且判断每个在亮灯泡的剩余时间是否足够其一直亮着

代码

from datetime import datetime, timedelta
from typing import List, Optional, Union, Tuple

def sum_light(els: List[Union[datetime, Tuple[datetime, int]]],
        start_watching: Optional[datetime] = None,
        end_watching: Optional[datetime] = None,
        operating: Optional[timedelta] = None) -> int:
    
    total_time_on = 0
    
    if start_watching == None: start_watching = datetime.min
    if end_watching == None: end_watching = datetime.max
    if operating == None:
        operating = (datetime.max - datetime.min).total_seconds()
    else:
        operating = operating.total_seconds()
        
    button_presses = [i if isinstance(i, tuple) else (i,1) for i in els]
    button_presses.sort()
    if len(button_presses)%2: button_presses.append((end_watching, 0))
        
    last_timestamp = button_presses[0][0]
    last_light_id  = button_presses[0][1]
    lights_on = [button_presses[0][1]]
    lights_time_on = {button_presses[0][1]:0}
    lights_rest_time = {button_presses[0][1]:operating}
        
    for timestamp, light_id in button_presses[1:]:
        if light_id not in lights_time_on:
            lights_time_on[light_id] = 0
            lights_rest_time[light_id] = operating
                
        light_time_on = (timestamp - last_timestamp).total_seconds()
        
        if lights_on:
            max_rest_time = max(lights_rest_time[i] for i in lights_on)
            if max_rest_time > 0:
                for k,v in lights_rest_time.items():
                    if v == max_rest_time:
                        key = k
            else:
                break
            
            if max_rest_time >= light_time_on:
                L = max(start_watching, last_timestamp)
                R = min(end_watching, timestamp)
                if L < R:
                    time_delta = (R-L).total_seconds()
                    total_time_on += time_delta
            else:
                total_time_on += max_rest_time
              
            for i in lights_on:    
                lights_time_on[i] += light_time_on
                if lights_rest_time[i] > light_time_on:
                    lights_rest_time[i] -= light_time_on
                else:
                    lights_rest_time[i] = 0
               
        if light_id in lights_on:
            lights_on.remove(light_id)
        elif lights_rest_time[light_id] > 0:
            lights_on.append(light_id)
                
        last_timestamp = timestamp
        last_light_id = light_id
    return total_time_on

优秀代码

No.1

from itertools import zip_longest

SECONDS_IN_DAY = 60 * 60 * 24
    
def get_periods(start_watching, end_watching, els, operating):
    for start, end in zip_longest(els[::2], els[1::2], fillvalue=end_watching):
        if start_watching > end:
            continue

        period = (min(max(start, start_watching), end_watching), min(end, end_watching))

        if operating is None:
            yield period
        else:
            period = (period[0], min(period[1], period[0] + operating))

            yield period

            operating -= period[1] - period[0]
            if not operating:
                break


def unite_timelines(timelines):
    periods = sum(timelines, [])
    periods = sorted(periods, key=lambda a: a[0])
    central = periods.pop(0)
    while periods:
        cur = periods.pop(0)
        if cur[0] > central[1]:
            yield central
            central = cur
            continue

        central = (min(central[0], cur[0]), max(central[1], cur[1]))

    yield central


def periods_to_seconds(periods):
    return sum([(t[1] - t[0]).seconds + (t[1] - t[0]).days * SECONDS_IN_DAY for t in periods])

def get_timelines(els):
    from collections import defaultdict
    timelines = defaultdict(list)
    for el in els:
        if isinstance(el, datetime):
            timelines[0].append(el)
        else:
            timelines[el[1]].append(el[0])
    return timelines.values()
    
def sum_light(els: List[Union[datetime,tuple[datetime, int]]],
                start_watching: datetime = datetime.min,
                end_watching: datetime = datetime.max,
                operating: Optional[timedelta] = None) -> int:
    timelines = get_timelines(els)
    periods = [list(get_periods(start_watching, end_watching, els, operating)) for els in timelines]
    periods = unite_timelines(periods)
    return periods_to_seconds(periods)

No.2

from collections import defaultdict

def sum_light(els: List[Union[datetime, tuple[datetime, int]]],
              start_watching: Optional[datetime] = None,
              end_watching: Optional[datetime] = None,
              operating: Optional[timedelta] = None) -> int:

    LIGHT_1 = -1
    END = datetime(9999, 12, 31, 23, 59, 59)
    
    events = defaultdict(list)
    for event in els:
        if type(event) == datetime:
            events[LIGHT_1].append(event)
        else:
            events[event[1]].append(event[0])
    
    intervals = set()
    for n in events.keys():
        remaining = operating
        if len(events[n]) % 2 == 1:
            if events[n][-1] < end_watching:
                events[n].append(end_watching)
            else:
                events[n].pop()
        for i in range(0, len(events[n]), 2):
            if operating and remaining <= timedelta(0):
                continue
            sp, ep = events[n][i], events[n][i + 1]
            if operating:
                ep = min(ep, sp + remaining)
                remaining -= (ep - sp)
            if (start_watching and ep <= start_watching) or (end_watching and end_watching <= sp):
                continue
            intervals.add((max(start_watching or sp, sp), min(end_watching or ep, ep)))
    if not intervals:
        return 0
    
    intervals = sorted(intervals, key=lambda x: (x[0], END - x[1]), reverse=True)
    new_intervals = [intervals.pop()]
    while intervals:
        sp, ep = intervals.pop()
        if new_intervals[-1][1] < ep:
            if new_intervals[-1][1] < sp:
                new_intervals.append((sp, ep))
            else:
                new_intervals[-1] = (new_intervals[-1][0], ep)
    
    res = sum(int((ep - sp).total_seconds()) for sp, ep in new_intervals)
    return res

No.3

from operator import itemgetter
from itertools import groupby

def sum_light(els: List[Union[datetime, Tuple[datetime, int]]],
        start_watching: Optional[datetime] = datetime.min,
        end_watching: Optional[datetime] = datetime.max,
        operating: Optional[timedelta] = timedelta.max) -> int:
    """
        how long the light bulb has been turned on
    """
    els = [(item, 1) if isinstance(item, datetime) else item for item in els]

    def make_intervals(dts):
        rest = operating
        for a, b in zip(dts[::2], dts[1::2] + [datetime.max]):
            yield (a, a + min(rest, b - a))
            rest -= b - a
            if rest <= timedelta(0):
                break

    def merge_intervals(intervals):
        try:
            (a, b), *other = intervals
            for c, d in other:
                if c > b:
                    yield (a, b)
                    a, b = c, d
                else:
                    b = max(b, d)
            yield (a, b)
        except ValueError:
            pass

    lamp = itemgetter(1)
    lamp_intervals = {k: list(make_intervals([dt for dt, _ in g]))
                      for k, g in groupby(sorted(els, key=lamp), key=lamp)}
    return sum((max(a, min(b, end_watching)) - min(b, max(a, start_watching))).total_seconds()
               for a, b in merge_intervals(sorted(sum(lamp_intervals.values(), []))))

No.4

def transform(els: List[Union[datetime, Tuple[datetime,int]]], operating: datetime):
    tels = []
    state = {}
    last = {}
    skip = {}
    for el in els:
        state[el[1]] = not state.get(el[1],False)
        if state[el[1]]:
            tels.append(el)
            last[el[1]] = el[0]
        elif skip.get(el[1],False):
            continue
        elif el[0] - last[el[1]] <= operating:
            tels.append(el)
        else:
            tels.append((last[el[1]] + operating,el[1]))
            skip[el[1]] = True
    return tels

def sum_light(els: List[Union[datetime, Tuple[datetime, int]]],
        start_watching: Optional[datetime] = None,
        end_watching: Optional[datetime] = None,
        operating: Optional[timedelta] = None) -> int:
    duration = 0
    if len(els) % 2 != 0:
        els.append(end_watching)
    els = [(el,1) if isinstance(el, datetime) else el for el in els]
    if operating is not None:
        els = transform(els, operating)
    els = sorted(els, key=lambda x: x[0])
    state = {}
    for i in range(0, len(els)-1):
        n1 = els[i][1]
        n2 = els[i+1][1]
        t1 = els[i][0] if start_watching is None or els[i][0] > start_watching else start_watching
        t2 = els[i+1][0] if end_watching is None or els[i+1][0] < end_watching else end_watching
        state[n1] = not state.get(n1,False)
        if any(state.values()):
            if operating is None:
                delta = (t2 - t1).total_seconds()
            else:
                delta = min(t2-t1,operating).total_seconds()
            duration += max(delta,0)
            
    return duration
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值