【672. 灯泡开关 Ⅱ】

这篇博客介绍了LeetCode上的一道题目,涉及如何通过按压4个不同功能的开关来控制n个灯泡的状态。博主提出了一种优化搜索空间的方法,将问题简化为考虑每个开关被按压的奇偶性,并通过分析开关效果将灯泡分为四组,最终只需检查4个灯泡的状态即可确定所有灯泡的状态。通过这种方法,实现了时间复杂度为O(1)和空间复杂度为O(1)的解决方案。
摘要由CSDN通过智能技术生成

来源:力扣(LeetCode)

描述:

房间中有 n 只已经打开的灯泡,编号从 1n 。墙上挂着 4 个开关

这 4 个开关各自都具有不同的功能,其中:

  • 开关 1 :反转当前所有灯的状态(即开变为关,关变为开)

  • 开关 2 :反转编号为偶数的灯的状态(即 2, 4, ...

  • 开关 3 :反转编号为奇数的灯的状态(即 1, 3, ...

  • 开关 4 :反转编号为 j = 3k + 1 的灯的状态,其中 k = 0, 1, 2, ...(即 1, 4, 7, 10, ...

你必须 恰好 按压开关 presses 次。每次按压,你都需要从 4 个开关中选出一个来执行按压操作。

给你两个整数 npresses ,执行完所有按压之后,返回 不同可能状态 的数量。

示例 1:

输入:n = 1, presses = 1
输出:2
解释:状态可以是:
- 按压开关 1[]
- 按压开关 2[]

示例 2:

输入:n = 2, presses = 1
输出:3
解释:状态可以是:
- 按压开关 1[,]
- 按压开关 2[,]
- 按压开关 3[,]

示例 3:

输入:n = 3, presses = 1
输出:4
解释:状态可以是:
- 按压开关 1[,,]
- 按压开关 2[,,]
- 按压开关 3[,,]
- 按压开关 4[,,]

提示:

  • 1 <= n <= 1000

  • 0 <= presses <= 1000

方法:降低搜索空间

思路

如果不进行任何优化进行搜索,需要按 presses 次,每次有 4 种选择,那么一共有 4presses 种按动选择,每种选择消耗 O(n) 时间计算状态,则最终的时间复杂度为 O(n × 4presses)。经过思考,可以从以下角度降低搜索空间。

首先,不需要考虑按钮按动的顺序,而只需要考虑每个按钮被按了几次,在按钮按动次数一样的情况下,顺序不影响灯泡最后的状态。更进一步地,不需要考虑每个按钮具体被按了几次,而只需要考虑被按了奇数次还是偶数次即可,某个键每多按或少按 2 次及其倍数次,也不影响最后的状态。

其次,观察每个按钮的效果,可以发现所有按钮可以根据编号划分为以下 4 组,周期为 6,下列编号中 k ≥ 0:

  • 编号为 6k + 1,受按钮 1, 3, 4 影响;

  • 编号为 6k + 2, 6k + 6,受按钮 1, 2 影响;

  • 编号为 6k + 3, 6k + 5,受按钮 1, 3 影响;

  • 编号为 6k + 4,受按钮 1, 2, 4 影响。

因此,只需要考虑四个灯泡,即可知道所有灯泡最后的状态了。

编写代码时,可以用一个长度为 4 数组 pressArr 表示 4 个按钮的按动情况。一个整数status 表示四组灯泡亮灭的状态。最后计算遇到过几种不同的状态即可。

代码:

class Solution {
public:
    int flipLights(int n, int presses) {
        unordered_set<int> seen;
        for (int i = 0; i < 1 << 4; i++) {
            vector<int> pressArr(4);
            for (int j = 0; j < 4; j++) {
                pressArr[j] = (i >> j) & 1;
            }
            int sum = accumulate(pressArr.begin(), pressArr.end(), 0);
            if (sum % 2 == presses % 2 && sum <= presses) {
                int status = pressArr[0] ^ pressArr[1] ^ pressArr[3];
                if (n >= 2) {
                    status |= (pressArr[0] ^ pressArr[1]) << 1;
                }
                if (n >= 3) {
                    status |= (pressArr[0] ^ pressArr[2]) << 2;
                }
                if (n >= 4) {
                    status |= (pressArr[0] ^ pressArr[1] ^ pressArr[3]) << 3;
                }
                seen.emplace(status);
            }
        }
        return seen.size();
    }
};

执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了49.36%的用户
复杂度分析
时间复杂度:O(1)。只需要使用常数时间即可完成计算。
空间复杂度:O(1)。只需要使用常数空间即可完成计算。
author:LeetCode-Solution

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值