O‘REILLY: Multiple Lightbulbs —— 多灯泡下计算指定时间范围内多组时间差之和(秒数)

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

背景

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

题目

这一任务是这个系列里的第四个任务。在前几个任务中,我们只考虑一个灯泡,这个任务里,我们考虑多个灯泡。

这一任务仍然要计算在 开始计时时间停止计算时间 内房间亮着的时间。但是这一次,我们有不止一个灯泡, 这意味着现在可以在灯泡开关列表中包含已按下按钮的灯泡的编号。

数组里的元素可以是 datetime 对象(默认是按下第一个按钮的时间),也可以是包含2个元素的元组(其中第一个元素是 datetime 对象,即按下按钮的时间),而第二个元素是灯泡的编号,从而确定是哪个灯泡。

如果传递的数组仅由 datetime 对象组成,那么只有一个灯泡,此时与前面任务的功能相同。
在这里插入图片描述

输入: 给定三个参数,第一个是必选参数,第二个和第三个是可选参数。第一个参数是时间格式组成的列表(也可以是由时间和整数组成的元组),第二个和第三个参数是时间。

输出: 秒数(整数)

举个栗子:

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),
]) == 60

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

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

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),
], datetime(2015, 1, 12, 10, 0, 20), datetime(2015, 1, 12, 10, 1, 0)) == 40

假设:

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

题目框架

from datetime import datetime
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) -> int:
    """
        how long the light bulb has been turned on
    """
    return 0

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

    print(sum_light(els=[
        (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),
    ], start_watching=datetime(2015, 1, 12, 10, 0, 0), end_watching=datetime(2015, 1, 12, 10, 1, 0)))

    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

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

难度: Simple+

思路及代码

思路

这个我的解法实在太麻烦了 😐

主要的思路是把所有灯泡的时间降为一维,标记空挡时间点,形成只有一个灯泡的形式,再利用上一个任务的指定时间范围内的多组时间差来计算

代码

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

    flatten_els = []
    #分灯泡个数考虑
    if all(not isinstance(i, tuple) for i in els):
        flatten_els = els
    else:
        # 修改列表中第一个灯泡为元组格式
        lightbulb = []
        for i in els:
            if not isinstance(i, tuple):
                lightbulb.append((i, 1))
            else:
                lightbulb.append(i)
        
        #按 灯泡——时间 排序
        lightbulb_sorted = sorted(lightbulb, key = lambda x:x[1])
    
        #标记转折点
        turn_point = []
        for i in range(1, len(lightbulb_sorted)-1, 2):
            if lightbulb_sorted[i][1] != lightbulb_sorted[i+1][1]:
                turn_point.append(i+1)
        for i in turn_point:
            if i%2:
                lightbulb_sorted.pop(i, (datetime.max,lightbulb_sorted[turn_point[i]-1][1]))
        if len(turn_point)%2:
            lightbulb_sorted.append((datetime.max,lightbulb_sorted[-1][1]))
        
        #flatten时间
        flatten_els = [lightbulb[0][0]]
        for i in range(1, len(lightbulb_sorted)):
            point = turn_point.copy()
            if i in point:
                item = True
            else:
                item = False
            point.append(i)
            point = sorted(point)
            #比较列表
            compare_list = []
            if item:
                index = point.index(i)+1
            else:
                index = point.index(i)
            if index == 0:
                compare_list = lightbulb_sorted[point[1]:]
            elif index == len(point)-1:
                compare_list = lightbulb_sorted[:point[index-1]]
            else:
                compare_list = lightbulb_sorted[:point[index-1]] + lightbulb_sorted[point[index+1]:]
            #比较
            ele = []
            for j in range(1, len(compare_list), 2):
                ele.append(compare_list[j-1][0] <= lightbulb_sorted[i][0] <= compare_list[j][0])
            if any(ele):
                continue
            else:
                if lightbulb_sorted[i][0] not in flatten_els:
                    flatten_els.append(lightbulb_sorted[i][0]) 
    
    result = 0
    flatten_els += [datetime.max]
    for start, end in zip(flatten_els[::2],flatten_els[1::2]):
        real_start = max(start, start_watching or start)
        real_end = min(end, end_watching or end)
        if real_start < real_end:
            result += (real_end - real_start).total_seconds()
    return result

优秀代码

No.1

def sum_light(els: List[Union[datetime, Tuple[datetime, int]]],
        start_watching: Optional[datetime] = None,
        end_watching: Optional[datetime] = None) -> int:
    def unify():
        lamps_on = set()
        for item in els:
            try:
                dt, lamp = item
            except TypeError:
                dt, lamp = item, 1
            if not lamps_on or (lamps_on == {lamp}):
                yield dt
            lamps_on ^= {lamp}
    els = list(unify())

    return sum(((max(a, min(b, end_watching)) if end_watching else b)
              - (min(b, max(a, start_watching)) if start_watching else a)).total_seconds()
               for a, b in zip_longest(els[::2], els[1::2], fillvalue=datetime.max))

评论区有人指出,可以用 dt, lamp = item if isinstance(item, tuple) else (item, 1) 代替 try except

No.2

def sum_light(button_presses, start_watching=None, end_watching=None) -> int:
    total_time_on = 0

    if start_watching is None: start_watching = datetime(1970,1,1,0,0,0)
    if end_watching is None: end_watching = datetime(9999,12,31,23,59,59)
    
    button_presses = [x if type(x) == tuple else (x,1) for x in button_presses]
    button_presses.sort()
    if len(button_presses) % 2 == 1: button_presses.append((end_watching,0))
        
    last_timestamp,lights_on = None,set()
    for timestamp,light_id in button_presses:
        if len(lights_on) > 0:
            L = max(start_watching,last_timestamp)
            R = min(end_watching,timestamp)
            if L < R:
                total_time_on += (R-L).total_seconds()
        if light_id in lights_on:
            lights_on.remove(light_id)
        else:
            lights_on.add(light_id)
        last_timestamp = timestamp

    return total_time_on
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值