一共4道编程题,限时120min。做了3道。
第一题。
小明早上上学定了N个闹钟,闹钟响起后,选择起床或继续睡等待下一个闹钟响起。已知上课时间和路上花费时间。求小明最晚在哪个闹钟的时间起床。
输入:
- N个闹钟
- 花费时间(分钟)
- 上课时间几时几分
- N行,每行都是一个闹钟的时分
输出:
最晚的闹钟的时分
例:
输入:
5
59
06 59
04 00
05 00
06 00
07 00
08 00
输出:
06 00
思路:
首先,用上课时间-花费时间得到最晚起床时间。然后对所有闹钟进行排序后,用二分查找得到最晚起床闹钟。
n = int(input().strip()) # 闹钟个数
clocks = []
for i in range(n):
clock = [int(x) for x in input().strip().split(" ")]
clocks.append(clock)
time = int(input().strip()) # 需要时间
deadline = [int(x) for x in input().strip().split(" ")] # 上课时间
# 上课时间-花费时间 = 最晚起床时间
h = deadline[0]
m = deadline[1] - time
while m < 0:
h -= 1
m += 60
deadline = [h, m]
clocks.sort(key=lambda x: (x[0], x[1]))
def less(a, b):
if a[0] == b[0]:
return a[1] < b[1]
else:
return a[0] < b[0]
def greater(a, b):
if a[0] == b[0]:
return a[1] > b[1]
else:
return a[0] > b[0]
def print_res(num):
print(str(num[0]) + " " + str(num[1]))
# 二分法查找
low, high = 0, n
while low <= high:
mid = (low + high) // 2
if less(clocks[mid], deadline): # 如果当前闹钟早于deadline
if mid < n - 1: # 如果不是最后一个闹钟
if greater(clocks[mid + 1], deadline): # 如果下一个闹钟晚于deadline
print_res(clocks[mid])
break
else:
low = mid + 1
else: # 是最后一个闹钟直接输出
print_res(clocks[mid])
break
elif greater(clocks[mid], deadline): # 如果当前闹钟晚于deadline
high = mid - 1
if high <= 0:
print_res(clocks[0])
break
else: # 如果当前闹钟等于deadline,直接输出
print_res(clocks[mid])
break
第二题
小明和小红发明了一种加密方法。对长度为N的"0/1"字符串S(明文),加密K次。第i次加密就右移i-1次。然后对这K个字符串求异或得到最终的密文。
例如S = “1001010”,K=4。如下
1001010
  
\;
1001010
  
\;
  
\;
1001010
  
\;
  
\;
  
\;
1001010
密文P = “1110100110”
要求写出密文的解密算法,得到明文。
输入:
1.两个数。明文长度N,加密次数K
2.密文
输出:
明文
例:
输入:
7 4
1110100110
输出:
1001010
思路
首先明白异或的性质
1.如果
A
⨁
B
=
C
A\bigoplus B = C
A⨁B=C, 那么
B
=
A
⨁
C
B = A\bigoplus C
B=A⨁C。相当于三个数已知两个数,就可以异或求第三个数。
2.任何数与0异或得到它本身。
已知密文P的长度是N-K+1。然后,从密文开始分析。比如上面的例子中,明文是1001010,密文是1110100110。那么密文的最后一位是由明文的最后一位与三个0异或得到的。那么密文的最后一位也就是明文的最后一位,由此得到明文的最后一位后。接下来密文的倒数第二位是两个0与明文的倒数第二位与明文的倒数第一位得到的。
也就是
0
⨁
0
⨁
S
(
N
−
2
)
⨁
S
(
N
−
1
)
=
P
(
N
+
K
−
3
)
0 \bigoplus 0 \bigoplus S(N-2)\bigoplus S(N-1) = P(N+K-3)
0⨁0⨁S(N−2)⨁S(N−1)=P(N+K−3)
由性质2,
S
(
N
−
2
)
⨁
S
(
N
−
1
)
=
P
(
N
+
K
−
3
)
S(N-2)\bigoplus S(N-1) = P(N+K-3)
S(N−2)⨁S(N−1)=P(N+K−3)
由性质1,
S
(
N
−
2
)
=
P
(
N
+
K
−
3
)
⨁
S
(
N
−
1
)
S(N-2)= P(N+K-3)\bigoplus S(N-1)
S(N−2)=P(N+K−3)⨁S(N−1)
同理有,
S
(
N
−
3
)
=
P
(
N
+
K
−
4
)
⨁
S
(
N
−
1
)
⨁
S
(
N
−
2
)
S(N-3)= P(N+K-4)\bigoplus S(N-1) \bigoplus S(N-2)
S(N−3)=P(N+K−4)⨁S(N−1)⨁S(N−2)
从后往前看,也就是明文的某个数等于它后面明文异或后,再异或上对应位置的密文。但是这个数往后取的位数最多不超过K-1位。比如上面的例子 S ( N − 5 ) = P ( N + K − 6 ) ⨁ S ( N − 2 ) ⨁ S ( N − 3 ) ⨁ S ( N − 4 ) ) S(N-5)= P(N+K-6)\bigoplus S(N-2) \bigoplus S(N-3) \bigoplus S(N-4) ) S(N−5)=P(N+K−6)⨁S(N−2)⨁S(N−3)⨁S(N−4))这里就舍去了 S ( N − 1 ) S(N-1) S(N−1)。
如果按照这样的方法,平均每解密一个字符需要进行K次异或操作,一共解密N个字符。所以时间复杂度是
O
(
K
N
)
O(KN)
O(KN)。
但这样做只通过了50%的case。因为时间复杂度过大导致超时了。
注意到:
S
(
N
−
5
)
=
P
(
N
+
K
−
6
)
⨁
S
(
N
−
2
)
⨁
S
(
N
−
3
)
⨁
S
(
N
−
4
)
S(N-5)= P(N+K-6)\bigoplus S(N-2) \bigoplus S(N-3) \bigoplus S(N-4)
S(N−5)=P(N+K−6)⨁S(N−2)⨁S(N−3)⨁S(N−4)
S
(
N
−
6
)
=
P
(
N
+
K
−
7
)
⨁
S
(
N
−
3
)
⨁
S
(
N
−
4
)
⨁
S
(
N
−
5
)
S(N-6)= P(N+K-7)\bigoplus S(N-3) \bigoplus S(N-4) \bigoplus S(N-5)
S(N−6)=P(N+K−7)⨁S(N−3)⨁S(N−4)⨁S(N−5)
将等式右边分为A、B两部分,A是对应位置的密文,另一部分B则是前K-1个明文的异或。发现每次B部分其实不需要计算K-1次,只需要用上一次的B部分去掉一个明文异或后再新异或一个明文即可。可是如何去掉一个异或呢,看到性质1,对于S(N-6)的等式上次的B部分B’为
B
′
=
S
(
N
−
2
)
⨁
S
(
N
−
3
)
⨁
S
(
N
−
4
)
B' = S(N-2) \bigoplus S(N-3) \bigoplus S(N-4)
B′=S(N−2)⨁S(N−3)⨁S(N−4),而本次的B部分
B
=
B
′
⨁
S
(
N
−
2
)
⨁
S
(
N
−
5
)
B = B'\bigoplus S(N-2) \bigoplus S(N-5)
B=B′⨁S(N−2)⨁S(N−5)。相当于每次只做了两次异或就得到了B部分,而再异或一次A部分就得到了本次的结果。即解密一次最多需要异或3次。而对于倒数K-1位之前的密文,则不需要在B’中去掉一部分。
def xor(a, b):
if a == b:
return "0"
else:
return "1"
N, K = [int(x) for x in input().strip().split(" ")]
P = input().strip()
S = ""
last_B = "0"
for i in range(K-1, N+K-1)[::-1]:
num = xor(last_B, P[i])
S = num + S
if len(S) >= K:
last_B = xor(last_B, S[K - 1])
last_B = xor(last_B, num)
print(S)
第三题
老板给员工发年终奖,员工坐成一排。
条件:1.每个员工至少100元年终奖。2.相邻的员工可以互相得知对方年终奖,在公司年份大的要比在公司年份小的至少多100元,员工才不会有意见。
问:老板怎么发年终奖满足以上条件,并且总和最少。
输入:
1.N个员工
2.每个员工的来公司的年份。
输出:
每个员工的年终奖
例1
输入:
5
7 3 9 6 2
输出:
200 100 300 200 100
例2
输入:
4
1 1 1 1
输出:
100 100 100 100
思路
对于每一个员工都得看他左右连续比他小的员工个数。然后取左右中的最大值。那么它的年终奖等于这个值*100。
N = int(input())
years = [int(x) for x in input().strip().split(" ")]
left = [1] * N #记录每个员工左边连续比他小的个数
right = [1] * N #记录每个员工右边连续比他小的个数
for i in range(1, N):
if years[i] > years[i - 1]:
left[i] += left[i-1]
for i in range(N-1)[::-1]:
if years[i] > years[i + 1]:
right[i] += right[i+1]
for i in range(N):
years[i] = str(max(left[i], right[i]) * 100)
print(" ".join(years))