问题描述:
VOS声控开关是一种很灵活的小设备,它的一边是插头,可以插到插座里;另一边是一个插座可以插一盏灯或者另一个VOS开关。
当VOS开关接通之后,它从插头获取电能,同时可以输出到插座里。当你打响指的时候 – 发出嗒声-- 通了电的VOS开关会在‘接通’和‘关闭’之间切换。
抱着通过一个奇点来毁灭整个宇宙的希望,我买了 N 个VOS开关,把它串了起来,第一个开关插入插座,第二个开关插到第一个VOS开关的插座上,以此类推。另有一盏灯插在第 N 个VOS开关上。
刚开始的时候,所有的开关都是关闭状态,所以此时只有第一个开关是通电的,而灯是不亮的。我打了一个响指之后,第一个开关被接通,第二个开关通电了并保持关闭状态。再打一次响指,触发两个VOS开关,第二个开关失去了电能,但是它已是开通状态。第三次响指之后,第一个VOS开关接通,给第二个开关通电。此时,两个VOS开关都是开通状态,如果有一个灯插在第二个开关上,那么它就被点亮了。
我就这样做了几个小时之后,在打了 K 次响指之后,最后的灯是点亮的,还是不亮的?灯只在它插在通电的VOS开关上才会被点亮。
输入:
输入的第一行表示测试用例的个数 T。之后的T行,每一行都包括两个整数, N 和 K。
输出:
针对每个测试用例,输出必须形如:'Case #x: y',其中 x 表示用例的需要(从1开始计数),而y 则要么是 ‘ON’ 要么是 ‘OFF’,表示电灯泡的状态。
Limits
1 ≤ T ≤ 10,000.
Small dataset
1 ≤ N ≤ 10;
0 ≤ K ≤ 100;
Large dataset
1 ≤ N ≤ 30;
0 ≤ K ≤ 108;
Sample
Input
4
1 0
1 1
4 0
4 47
Output
Case #1: OFF
Case #2: ON
Case #3: OFF
Case #4: ON
首先,针对问题进行分析:
通过对题目的分析,我们可以基本得出结论:
当且仅当所有的开关均是闭合状态,指示灯亮。于是我们可以在每次响指后,判断所有开关的闭合状态,从而得出指示灯的状态。
代码实现如下(python 语言):说明代码实现需要将输入文件的第一行去掉。
#!/usr/bin/python
# -*- coding:utf-8 -*-
import string
def compute_result(switch_num, action_num):
#init all the switch state in state_list
state_list = list()
for i in range(switch_num):
state_list.append(0)
#update state_list after action
for i in range(action_num):
connect_num = 1
for state in state_list:
if state == 1:
connect_num += 1
else:
break
if connect_num > len(state_list):
connect_num = len(state_list)
for k in range(connect_num):
if state_list[k] == 1:
state_list[k] = 0
else:
state_list[k] = 1
#compute result
if 0 not in state_list:
return "ON"
return "OFF"
if __name__ == "__main__":
k = 1
out_result = ""
input_file = "Chain.large.1496809484823.input.txt"
with open (input_file, "r") as fread:
for line in fread.readlines():
switch_num, action_num = line.split(" ")
print switch_num, action_num
result = compute_result(string.atoi(switch_num), string.atoi(action_num))
out_result += "Case #%s: %s\n" % (k, result)
k += 1
# write result to out file
with open("%s_output.txt" % input_file, "w") as fwrite:
fwrite.write(out_result)
运行发现,如果运行大数据,比如响指次数达几千万次,将耗时很多,大概计算了下如果有一万组数据,就会耗时多大几十个小时。
优化1: 观察到第一个开关始终是通电的,那如果响指次数为偶数的话,第一个开关应该是打开的,那么指示灯一定是不亮的,那么优化的代码加了一句:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import string
def compute_result(switch_num, action_num):
if action_num % 2 == 0:
return "OFF"
#init all the switch state in state_list
state_list = list()
for i in range(switch_num):
state_list.append(0)
#update state_list after action
for i in range(action_num):
connect_num = 1
for state in state_list:
if state == 1:
connect_num += 1
else:
break
if connect_num > len(state_list):
connect_num = len(state_list)
for k in range(connect_num):
if state_list[k] == 1:
state_list[k] = 0
else:
state_list[k] = 1
#compute result
if 0 not in state_list:
return "ON"
return "OFF"
if __name__ == "__main__":
k = 1
out_result = ""
input_file = "Chain.large.1496809484823.input.txt"
with open (input_file, "r") as fread:
for line in fread.readlines():
switch_num, action_num = line.split(" ")
print switch_num, action_num
result = compute_result(string.atoi(switch_num), string.atoi(action_num))
out_result += "Case #%s: %s\n" % (k, result)
k += 1
# write result to out file
with open("%s_output.txt" % input_file, "w") as fwrite:
fwrite.write(out_result)
这样运行之后,确实响指偶数确实很快返回了结果,但是理论上只会缩短一半的时间,耗时仍然比较长。
那有没有更加有效的方法呢?下面我们就来仔细分析下。
以两个开关为例,分析过程如下图(要特别注意的一点是问题描述中着重说明了通了电的开关会在‘接通’和‘关闭’之间切换):
说明:0 代表初始状态, 1,2,…代表第i次响指后的状态。红线代表通电。
结论:两个开关,最少需要3次响指,指示灯才会亮, 第4次响指后开关恢复初始状态
所以两个开关的情况下使得指示灯亮的响指次数为3,7,11,15……,最少响指次数为3
下面,同样分析三个开关的情况,如下图过程:
结论:三个开关需要7次响指才会使得指示灯亮,第8次响指后开关恢复初始状态。
所以三个开关的情况下使得指示灯亮的响指次数为7,15,23,31,….,最少响指次数为7次。
总结如下表
开关个数 | 使得指示灯亮的最少响指次数 | 使得指示灯亮的响指次数 |
1 | 1 | 1, 3, 5, 7…. |
2 | 3 | 3, 7, 11, 15, …. |
3 | 7 | 7, 15, 23, 31, … |
4 | 15(24-1或者2*7+1) | 15, 31, 47, 63, … |
… | … | ….. |
N | M=2N-1或者2*(N-1开关的值)+1 | M或者M+2N M+2N+1 …… |
优化2:代码实现如下:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import string
import math
def compute_result(switch_num, action_num, result):
if action_num % 2 == 0:
return "OFF"
if action_num < result[switch_num]:
return "OFF"
elif action_num == result[switch_num]:
return "ON"
else:
if action_num % (math.pow(2, switch_num)) == result[switch_num]:
return "ON"
return "OFF"
def compute_min_num():
result = dict()
min_num = 1
for i in range(1,500):
result[i] = min_num
min_num = min_num * 2 + 1
return result
if __name__ == "__main__":
k = 1
out_result = ""
result1 = compute_min_num()
print "result= %s" % result1
input_file = "Chain.large.1496809484823.input.txt"
with open (input_file, "r") as fread:
for line in fread.readlines():
switch_num, action_num = line.split(" ")
print switch_num, action_num
result = compute_result(string.atoi(switch_num), string.atoi(action_num), result1)
out_result += "Case #%s: %s\n" % (k, result)
k += 1
# write result to out file
with open("%s_output.txt" % input_file, "w") as fwrite:
fwrite.write(out_result)
结果如下