程序员的算法趣题2
上QQ阅读APP看书,第一时间看更新

Q 04 电子钟的亮灯数

IQ 70
目标时间:15 分钟

有一些电子钟使用了如图 1.6 中左图所示的七段数码管。这类时钟会根据时间决定显示屏上亮灯的数量。例如,在 12 时 34 分 56 秒时,电子钟会像图 1.6 中右图那样有 27 处亮灯。

图 1.6 七段数码管的亮灯位置

这里我们反过来进行思考,试着通过亮灯的数量来判断时间。但要注意,有些数字的排列组合并不能用来表示时间,例如 53:61:24 也有 27 处亮灯,但它就不在我们考虑的范围内。

另外,这类电子钟是 24 小时制,可以显示到 23 时 59 分 59 秒,再过 1 秒钟,时、分、秒就会都变为 0。

问题

当有 30 处亮灯时,能表示的时间有多少个?

思路

有好几种方法可以用来数电子钟亮灯处的数量。电子屏上有 6 个显示数字的地方,每个显示数字的地方可有 7 处亮灯,所以一共有处可亮灯。要查询这 42 处中有 30 处亮灯的情况,其实就是求从 42 个里选 30 个地方亮灯,有多少种排列组合的方式。这会是一个非常大的数。

但是,只有能用来表示时间的组合才会被显示出来,所以组合方式的数量要在可表示的时间的范围内计算。计算出来之后,再统计有多少处亮灯就可以得到答案了。

在求可以表示多少个时间时,程序可以写成代码清单 04.01 和代码清单 04.02 这样。

代码清单 04.01q04_1.rb

N = 30

# 返回两位数的亮灯数
def check(num)
  light = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6]
  light[num / 10] + light[num % 10]
end

cnt = 0
24.times do |h|
  60.times do |m|
    60.times do |s|
      cnt += 1 if check(h) + check(m) + check(s) == N
    end
  end
end
puts cnt

代码清单 04.02q04_1.js

N = 30;

// 返回两位数的亮灯数
function check(num){
  var light = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6];
  return light[Math.floor(num / 10)] + light[num % 10];
}

var cnt = 0;
for (var h = 0; h < 24; h++){
  for (var m = 0; m < 60; m++){
    for (var s = 0; s < 60; s++){
      if (check(h) + check(m) + check(s) == N)
        cnt++;
    }
  }
}
console.log(cnt);

事先把已经计算过的结果存储起来,可以有效避免相同的数字被重复计算。代码清单 04.03 和代码清单 04.04 中就使用了这种方法,事先把 0 ~ 59 的数字存储到了数组中备用。

代码清单 04.03q04_2.rb

N = 30

# 返回两位数的亮灯数
def check(num)
  light = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6]

  light[num / 10] + light[num % 10]
end
lights = Array.new(60)
60.times do |i|
  lights[i] = check(i)
end

cnt = 0

24.times do |h|
  60.times do |m|
    60.times do |s|
      cnt += 1 if lights[h] + lights[m] + lights[s] == N
    end
  end
end
puts cnt

代码清单 04.04q04_2.js

N = 30;

// 返回两位数的亮灯个数
function check(num){
  var light = [6, 2, 5, 5, 4, 5, 6, 3, 7, 6];
  return light[Math.floor(num / 10)] + light[num % 10];
}

var lights = new Array(60);
for (var i = 0; i < 60; i++){
  lights[i] = check(i);
}

var cnt = 0;
for (var h = 0; h < 24; h++){
  for (var m = 0; m < 60; m++){
    for (var s = 0; s < 60; s++){
      if (lights[h] + lights[m] + lights[s] == N)
        cnt++;
    }
  }
}
console.log(cnt);

关键点

除了函数调用,数据库取值和文件导入等耗时的处理也可以事先执行。这种做法在实际工作中经常用到。

答案 8360 种