当前位置: 首页 > news >正文

位运算题目:N 天后的牢房

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:N 天后的牢房

出处:957. N 天后的牢房

难度

5 级

题目描述

要求

8 \texttt{8} 8 间牢房排成一排,每间牢房的状态是被占用或空置。

每天,无论牢房是被占用或空置,都会根据以下规则进行更改:

  • 如果一间牢房的两个相邻的房间都被占用或都被空置,那么该牢房就会被占用。
  • 否则,它就会被空置。

由于监狱中的牢房排成一行,所以行中的第一个和最后一个房间无法有两个相邻的房间。

给定一个整数数组 cells \texttt{cells} cells,其中 cells[i]   =   1 \texttt{cells[i] = 1} cells[i] = 1 表示第 i \texttt{i} i 间牢房被占用, cells[i]   =   0 \texttt{cells[i] = 0} cells[i] = 0 表示第 i \texttt{i} i 间牢房被空置。另外给定一个整数 n \texttt{n} n

返回 n \texttt{n} n 天后的监狱的状态(即 n \texttt{n} n 次上述描述的更改之后)。

示例

示例 1:

输入: cells   =   [0,1,0,1,1,0,0,1],   n   =   7 \texttt{cells = [0,1,0,1,1,0,0,1], n = 7} cells = [0,1,0,1,1,0,0,1], n = 7
输出: [0,0,1,1,0,0,0,0] \texttt{[0,0,1,1,0,0,0,0]} [0,0,1,1,0,0,0,0]
解释:以下是监狱每天的状态:
0 \texttt{0} 0 天: [0,1,0,1,1,0,0,1] \texttt{[0,1,0,1,1,0,0,1]} [0,1,0,1,1,0,0,1]
1 \texttt{1} 1 天: [0,1,1,0,0,0,0,0] \texttt{[0,1,1,0,0,0,0,0]} [0,1,1,0,0,0,0,0]
2 \texttt{2} 2 天: [0,0,0,0,1,1,1,0] \texttt{[0,0,0,0,1,1,1,0]} [0,0,0,0,1,1,1,0]
3 \texttt{3} 3 天: [0,1,1,0,0,1,0,0] \texttt{[0,1,1,0,0,1,0,0]} [0,1,1,0,0,1,0,0]
4 \texttt{4} 4 天: [0,0,0,0,0,1,0,0] \texttt{[0,0,0,0,0,1,0,0]} [0,0,0,0,0,1,0,0]
5 \texttt{5} 5 天: [0,1,1,1,0,1,0,0] \texttt{[0,1,1,1,0,1,0,0]} [0,1,1,1,0,1,0,0]
6 \texttt{6} 6 天: [0,0,1,0,1,1,0,0] \texttt{[0,0,1,0,1,1,0,0]} [0,0,1,0,1,1,0,0]
7 \texttt{7} 7 天: [0,0,1,1,0,0,0,0] \texttt{[0,0,1,1,0,0,0,0]} [0,0,1,1,0,0,0,0]

示例 2:

输入: cells   =   [1,0,0,1,0,0,1,0],   n   =   1000000000 \texttt{cells = [1,0,0,1,0,0,1,0], n = 1000000000} cells = [1,0,0,1,0,0,1,0], n = 1000000000
输出: [0,0,1,1,1,1,1,0] \texttt{[0,0,1,1,1,1,1,0]} [0,0,1,1,1,1,1,0]

数据范围

  • cells.length = 8 \texttt{cells.length} = \texttt{8} cells.length=8
  • cells[i] \texttt{cells[i]} cells[i] 的值为 0 \texttt{0} 0 1 \texttt{1} 1
  • 1 ≤ n ≤ 10 9 \texttt{1} \le \texttt{n} \le \texttt{10}^\texttt{9} 1n109

解法

思路和算法

为了得到 n n n 天后的每间牢房的状态,需要根据规则模拟牢房状态的变化。由于 n n n 的最大值是 1 0 9 10^9 109,直接模拟的时间复杂度过高,因此需要考虑时间复杂度更低的做法。

将牢房的初始状态记为第 0 0 0 天的状态,变化 k k k 次之后为第 k k k 天。由于第一个和最后一个房间无法有两个相邻的房间,因此根据规则,从第 1 1 1 天开始,第一个和最后一个房间的状态一定是空置,其余 6 6 6 个房间的状态可能是被占用或空置,不同的状态总数是 2 6 = 64 2^6 = 64 26=64 种,加上初始状态最多有 65 65 65 种状态。由于不同的状态数非常有限,因此在牢房状态的变化过程中一定会出现与之前重复的状态,即出现循环。假设第 x x x 天和第 y y y 天的牢房状态相同, x < y x < y x<y,则对于任意非负整数 k k k 都有第 x + k x + k x+k 天和第 y + k y + k y+k 天的牢房状态相同,即牢房状态的循环周期是 y − x y - x yx 天,循环周期一定不超过 65 65 65 天。对于牢房的任意初始状态,实际循环周期都不超过 14 14 14 天。

为了根据循环周期得到第 n n n 天的牢房状态,需要使用两个哈希表分别记录每个牢房状态首次出现的天数以及每天对应的牢房状态。由于题目给定的牢房状态是数组的形式,数组不适合存入哈希表,因此需要使用位运算将数组表示的牢房状态转换成整数表示的牢房状态,整数为 8 8 8 位二进制数,从低到高的第 0 0 0 位到第 7 7 7 位分别表示第 0 0 0 个到第 7 7 7 个房间的状态。

从牢房的初始状态开始模拟,计算每天的牢房状态并将状态和天数分别存入两个哈希表,当遇到结束条件时,结束模拟并计算第 n n n 天的牢房状态。

  • 如果到达第 n n n 天,则当前的牢房状态是第 n n n 天的牢房状态,将当前的牢房状态转成数组的形式返回。

  • 如果遇到一个状态是之前已经出现过的,则当前状态的首次出现即为循环的开始时间。将当前天数记为 day \textit{day} day,将当前状态的首次出现(即哈希表中记录的当前状态对应的天数)记为 start \textit{start} start,则循环周期 cycle = day − start \textit{cycle} = \textit{day} - \textit{start} cycle=daystart。计算第 n n n 天的牢房状态的方法如下。

    1. n n n 天与循环的开始时间的天数之差为 n − start n - \textit{start} nstart,该天数之差除以循环周期的余数是 remainder = ( n − start )   m o d   cycle \textit{remainder} = (n - \textit{start}) \bmod \textit{cycle} remainder=(nstart)modcycle

    2. n n n 天与第 start + remainder \textit{start} + \textit{remainder} start+remainder 天的天数之差为 cycle \textit{cycle} cycle 的整数倍,即从第 start + remainder \textit{start} + \textit{remainder} start+remainder 天到第 n n n 天经过若干个完整的周期且没有余数,因此第 n n n 天的牢房状态与第 start + remainder \textit{start} + \textit{remainder} start+remainder 天的牢房状态相同。

    3. 由于 remainder < cycle \textit{remainder} < \textit{cycle} remainder<cycle,因此哈希表中一定记录了第 start + remainder \textit{start} + \textit{remainder} start+remainder 天的牢房状态,从哈希表中获得第 start + remainder \textit{start} + \textit{remainder} start+remainder 天的牢房状态,将其转换成数组的形式返回。

代码

class Solution {
    static final int LENGTH = 8;

    public int[] prisonAfterNDays(int[] cells, int n) {
        Map<Integer, Integer> stateDayMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> dayStateMap = new HashMap<Integer, Integer>();
        int state = 0;
        for (int i = 0; i < LENGTH; i++) {
            state += cells[i] << i;
        }
        int day = 0;
        stateDayMap.put(state, day);
        dayStateMap.put(day, state);
        while (day < n) {
            state = getNextState(state);
            day++;
            if (!stateDayMap.containsKey(state)) {
                stateDayMap.put(state, day);
                dayStateMap.put(day, state);
            } else {
                break;
            }
        }
        if (day == n) {
            return stateToArr(state);
        }
        int start = stateDayMap.get(state);
        int cycle = day - start;
        int remainder = (n - start) % cycle;
        int lastState = dayStateMap.get(start + remainder);
        return stateToArr(lastState);
    }

    public int getNextState(int state) {
        int nextState = 0;
        for (int i = 1; i < LENGTH - 1; i++) {
            int prev = (state >> (i - 1)) & 1;
            int next = (state >> (i + 1)) & 1;
            if (prev == next) {
                nextState += 1 << i;
            }
        }
        return nextState;
    }

    public int[] stateToArr(int state) {
        int[] arr = new int[LENGTH];
        for (int i = 0; i < LENGTH; i++) {
            arr[i] = (state >> i) & 1;
        }
        return arr;
    }
}

复杂度分析

  • 时间复杂度: O ( 2 m × m ) O(2^m \times m) O(2m×m),其中 m m m 是数组 cells \textit{cells} cells 的长度, m = 8 m = 8 m=8。不同的状态总数不超过 2 m 2^m 2m,每种状态只会遍历一次,每次得到下一个状态需要 O ( m ) O(m) O(m) 的时间,遍历所有的状态需要 O ( 2 m × m ) O(2^m \times m) O(2m×m) 的时间,生成答案数组需要 O ( m ) O(m) O(m) 的时间,因此时间复杂度是 O ( 2 m × m ) O(2^m \times m) O(2m×m)

  • 空间复杂度: O ( 2 m ) O(2^m) O(2m),其中 m m m 是数组 cells \textit{cells} cells 的长度, m = 8 m = 8 m=8。空间复杂度主要取决于两个哈希表,每个哈希表中的记录个数不超过 2 m 2^m 2m 个。

相关文章:

  • [数据集]The Natural Scenes Dataset (NSD)介绍,申请及使用方法
  • 【蓝桥杯】算法笔记6
  • CubeMX配置STM32VET6实现网口通信(无操作系统版-附源码)
  • win11 nacos2.2.1 报错curl -X post 报错找不到和名称X匹配参数;不是命令
  • excel经验
  • (三十七)Dart 中使用 Pub 包管理系统与 HTTP 请求教程
  • leetcode0410. 分割数组的最大值-hard
  • 程序化广告行业(69/89):DMP与PCP系统核心功能剖析
  • STM32看门狗应用实战:独立看门狗与窗口看门狗深度解析(下) | 零基础入门STM32第九十五步
  • 详细介绍javaspringboot操控redis的高级特性1. 事务支持2. 发布/订阅3. Pipeline批量操作
  • git rebase复杂场景验证
  • QtConcurrent
  • COBOL语言的网络安全
  • AGI大模型(11):RAG系统
  • 在全球化浪潮下如何加强供应链风险管理?
  • Java全栈面试宝典:JMM内存模型与Spring自动装配深度解析
  • 数据结构实验3.1:顺序栈的基本操作与进制转换
  • 2025 年山东保安员职业资格考试要点梳理​
  • 深度学习篇---num_works选择
  • 【python以打包的形式运行和脚本形式运行获取路径注意事项】
  • 网站建设规划/网站优化的关键词
  • 百度网站名称和网址/seo建站要求
  • 设计师必看的10个网站/创建免费网站
  • 慈溪建设企业网站/福州专业的seo软件
  • 阿里云的网站程序如何做/影视剪辑培训机构排名
  • 珠海网站建设培训学校/品牌推广包括哪些内容