题目地址:
https://leetcode.com/problems/binary-watch/
有一个二进制LED灯的手表,给定亮灯数,返回其可能代表的时间。我们需要对小时和分钟分开来考虑。首先我们要构造一个函数,返回如果有 k k k个灯亮,那么它可能代表哪些数。
思路是DFS。由于 59 = ( 111011 ) 2 59=(111011)_2 59=(111011)2,所以我们要枚举 6 6 6个灯位,从第 0 0 0个灯开始枚举,枚举它亮还是不亮;如果枚举到了 k k k个灯亮的情形,就将其代表的数加入一个List中。接着只需要小时和分钟都各自遍历List即可。代码如下:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Solution {
public List<String> readBinaryWatch(int num) {
List<String> res = new ArrayList<>();
// 用一个map记录k个灯亮可能代表的数字是哪些
Map<Integer, List<Integer>> memo = new HashMap<>();
// 开始枚举小时有多少灯,分钟有多少灯。小时最多四盏灯
for (int i = 0; i <= Math.min(4, num); i++) {
for (int hour : generateNums(i, memo)) {
// 如果小时数合法,就枚举分钟
if (hour <= 11) {
for (int min : generateNums(num - i, memo)) {
res.add(String.format("%d:%02d", hour, min));
}
}
}
}
return res;
}
// 返回n个灯亮的时候,可能代表的数字
private List<Integer> generateNums(int n, Map<Integer, List<Integer>> memo) {
List<Integer> nums = memo.get(n);
if (nums == null) {
nums = new ArrayList<>();
dfs(nums, n, 0, 0, 0);
// 如果没有记忆就加入记忆
memo.put(n, nums);
}
return nums;
}
// n代表亮灯数,pos代表枚举的亮灯的初始位置,sum代表当前得到的数字,count代表sum这个数字有几个灯亮着
private void dfs(List<Integer> nums, int n, int pos, int sum, int count) {
// 如果已经有n个灯亮着,说明这是个合法的结果,将其加入nums里并返回
if (count == n) {
nums.add(sum);
return;
}
// 否则从pos开始枚举亮灯,如果还剩下的灯数不足,就不必枚举了
for (int i = pos; i < 6 - (n - count) + 1; i++) {
// 枚举第i位亮灯
sum += 1 << i;
// 如果第i位亮灯算出的sum小于60,说明还可以继续亮灯,进入下一层搜索
if (sum < 60) {
dfs(nums, n, i + 1, sum, count + 1);
}
// 恢复现场
sum -= 1 << i;
}
}
}