Leetcode-957 N 天后的牢房 Java 详细注释

8 间牢房排成一排,每间牢房不是有人住就是空着。

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

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

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

我们用以下方式描述监狱的当前状态:如果第 i 间牢房被占用,则 cell[i]==1,否则 cell[i]==0

根据监狱的初始状态,在 N 天后返回监狱的状况(和上述 N 种变化)。

 

示例 1:

输入:cells = [0,1,0,1,1,0,0,1], N = 7
输出:[0,0,1,1,0,0,0,0]
解释:
下表概述了监狱每天的状况:
Day 0: [0, 1, 0, 1, 1, 0, 0, 1]
Day 1: [0, 1, 1, 0, 0, 0, 0, 0]
Day 2: [0, 0, 0, 0, 1, 1, 1, 0]
Day 3: [0, 1, 1, 0, 0, 1, 0, 0]
Day 4: [0, 0, 0, 0, 0, 1, 0, 0]
Day 5: [0, 1, 1, 1, 0, 1, 0, 0]
Day 6: [0, 0, 1, 0, 1, 1, 0, 0]
Day 7: [0, 0, 1, 1, 0, 0, 0, 0]

示例 2:

输入:cells = [1,0,0,1,0,0,1,0], N = 1000000000
输出:[0,0,1,1,1,1,1,0]

 

提示:

  1. cells.length == 8
  2. cells[i] 的值为 01 
  3. 1 <= N <= 10^9

解法

  显然,从第1天开始,数组的头尾永远都是0,中间的6位则可以由左右两位同或得到。又注意到cells数组的长度永远是8,于是我想到用字节来存储每天的状态,然后专门写一个函数adjustByte(byte arrByte)来计算下一天对应的字节。显然,对于固定的输入,adjustByte(byte arrByte)函数会给出固定的输出,所以一旦发现计算出了重复的结果,就说明出现了循环。可以用一个ArrayList记录状态的出现顺序,用一个布尔数组记录每种状态是否已经出现过。由于头尾固定为0,状态至多有26=64种;不过为方便起见我还是使用了长度为8的布尔数组。

  需要注意的是,并不是所有出现过的状态都在循环节中,在循环出现之前可能有一部分状态只会出现一次,因此在发现循环之后要注意对这一部分进行处理。

  Leetcode的评论区许多人提到循环节长度固定为14的倍数,经测试确实如此。暂时不知道如何将这一规律推导出来,当然只为了减小运行时间的话归纳出了直接用就是了……

 

代码

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 /**
 5  *
 6  * @author yuan
 7  * @version 0.1
 8  * @date 2019/3/18
 9  */
10 public class Leetcode957 {
11 
12     private static byte adjustByte(byte arrByte) {
13         // 隔两位异或
14         arrByte ^= (arrByte << 2);
15         // 取反得到同或值
16         arrByte = (byte) ~arrByte;
17         // 无符号左移一位使高位清零 &255是为了处理类型提升为int的问题
18         arrByte = (byte) ((arrByte & 255) >>> 1);
19         // 最低位清零
20         arrByte &= ~1;
21         return arrByte;
22     }
23     
24     public static int[] prisonAfterNDays(int[] cells, int n) {
25         // 标记状态是否已经出现过
26         boolean[] map = new boolean[256];
27         // 按顺序存储出现的状态
28         List<Integer> list = new ArrayList<>();
29         // 用于表示状态的字节
30         byte arrByte = 0;
31         // 将数组转为字节
32         for (int i = 0; i < 8; i++) {
33             arrByte |= cells[i] << (7 - i);
34         }
35         // 按天计算
36         for (int i = 0; i < n; i++) {
37             // 出现重复,即出现循环 &0xff是为了将int转为无符号byte
38             if (map[arrByte & 0xff]) {
39                 // 获取循环开始下标
40                 int loopStart = list.indexOf((int) arrByte);
41                 // 计算循环节长度
42                 int loopLength = list.size() - loopStart;
43                 // 计算非循环部分长度
44                 int nonLoop = list.size() - loopLength;
45                 // 除去非循环部分后,从循环部分中获取正确的byte 先强转int拆箱再强转byte
46                 arrByte = (byte) (int) list.get((n - nonLoop) % loopLength + nonLoop);
47                 break;
48             }
49             map[arrByte & 0xff] = true;
50             list.add((int) arrByte & 0xff);
51             arrByte = adjustByte(arrByte);
52         }
53         // 结果数组
54         int[] result = new int[8];
55         // 将状态字节转为数组
56         for (int i = 0; i < 8; i++) {
57             result[i] = arrByte >> (7 - i) & 1;
58         }
59         return result;
60     }
61 }

 

坑点

  java自动类型提升问题+符号位问题

 

运行结果

  AC 21 ms 42.3 MB

转载于:https://www.cnblogs.com/null-0/p/10555197.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值