1. 5键键盘
1.1. 题目描述
有一个特殊的5键键盘,上面有a,ctrl-c,ctrl-x,ctrl-v,ctrl-a五个键。
a键在屏幕上输出一个字母a;
ctrl-c将当前选择的字母复制到剪贴板;
ctrl-x将当前选择的字母复制到剪贴板,并清空选择的字母;
ctrl-v将当前剪贴板里的字母输出到屏幕;
ctrl-a选择当前屏幕上的所有字母。
注意:
剪贴板初始为空,新的内容被复制到剪贴板时会覆盖原来的内容
当屏幕上没有字母时,ctrl-a无效
当没有选择字母时,ctrl-c和ctrl-x无效
当有字母被选择时,a和ctrl-v这两个有输出功能的键会先清空选择的字母,再进行输出
给定一系列键盘输入,输出最终屏幕上字母的数量。
1.2. 输入描述
输入为一行,为简化解析,用数字1 2 3 4 5代表a,ctrl-c,ctrl-x,ctrl-v,ctrl-a五个键的输入,数字用空格分隔。
输出描述
输出一个数字,为最终屏幕上字母的数量。
1.3. 题目解析
逻辑题,主要考察多情况的处理。
题目中没有准确说明 选择状态 何时被解除,比如我ctrl-a全选所有字母时,然后ctrl-c将选择的字母复制到剪贴板,那么此时屏幕中字母的选中状态是保留还是清除呢?
我理解ctrl-x剪切走屏幕内容,没有字母了,自然就没有选中状态了。另外,a、ctrl-v输入时,如果有字母选中状态,则输入时会覆盖选中内容,那么选中状态就没了。
1.4. 代码
class FiveKeyboards:
def __init__(self):
self.selected = ''
self.copyboard = ''
self.op_map = {
'1': self.a,
'2': self.ctrl_c,
'3': self.ctrl_x,
'4': self.ctrl_v,
'5': self.ctrl_a,
}
def a(self, string: str):
if not self.selected:
return string + 'a'
return 'a'
def ctrl_c(self, string: str):
if not self.selected:
return string
self.copyboard = self.selected
return string
def ctrl_x(self, string: str):
if not self.selected:
return string
self.copyboard = self.selected
return ''
def ctrl_v(self, string: str):
if not self.selected:
return string + self.copyboard
self.selected = ''
return self.copyboard
def ctrl_a(self, string: str):
self.selected = string
return string
def __call__(self, ops: str):
# 用数字1 2 3 4 5代表a,ctrl-c,ctrl-x,ctrl-v,ctrl-a五个键的输入
if '1' not in ops:
return 0
string = ''
for op in ops:
func = self.op_map.get(op)
if not func:
continue
string = func(string)
print(f"{func.__name__}: {string}")
return len(string)
2. 爱吃蟠桃的孙悟空
2.1. 题目描述
孙悟空爱吃蟠桃,有一天趁着蟠桃园守卫不在来偷吃。已知蟠桃园有 N 棵桃树,每颗树上都有桃子,守卫将在 H 小时后回来。
孙悟空可以决定他吃蟠桃的速度K(个/小时),每个小时选一颗桃树,并从树上吃掉 K 个,如果树上的桃子少于 K 个,则全部吃掉,并且这一小时剩余的时间里不再吃桃。
孙悟空喜欢慢慢吃,但又想在守卫回来前吃完桃子。
请返回孙悟空可以在 H 小时内吃掉所有桃子的最小速度 K(K为整数)。如果以任何速度都吃不完所有桃子,则返回0。
2.2. 输入描述
第一行输入为 N 个数字,N 表示桃树的数量,这 N 个数字表示每颗桃树上蟠桃的数量。
第二行输入为一个数字,表示守卫离开的时间 H。
其中数字通过空格分割,N、H为正整数,每颗树上都有蟠桃,且 0 < N < 10000,0 < H < 10000。
2.3. 输出描述
吃掉所有蟠桃的最小速度 K,无解或输入异常时输出 0。
2.4. 用例
2.5. 题目解析
遍历吃桃速度,找到最小可行解。为了优化该操作可以采用二分法求解
2.6. 代码
class Peach:
def can_finish(self, speed: int, tree_cnts: List[int], time_limit: int):
cost = 0
for tree_cnt in tree_cnts:
cost += tree_cnt // speed + (tree_cnt % speed > 0)
return cost <= time_limit
def __call__(self, tree_cnts: List[int], time_limit: int):
if len(tree_cnts) > time_limit:
return 0
max_speed = speed = max(tree_cnts)
if len(tree_cnts) == time_limit:
return max_speed
min_speed = 1
while min_speed < max_speed:
mid_speed = (min_speed + max_speed) // 2
if self.can_finish(mid_speed, tree_cnts, time_limit):
speed = mid_speed
max_speed = mid_speed - 1
else:
min_speed = mid_speed + 1
return speed
3. 按身高和体重排队
3.1. 题目描述
某学校举行运动会,学生们按编号(1、2、3…n)进行标识,现需要按照身高由低到高排列,对身高相同的人,按体重由轻到重排列;对于身高体重都相同的人,维持原有的编号顺序关系。请输出排列后的学生编号。
3.2. 输入描述
两个序列,每个序列由n个正整数组成(0 < n <= 100)。第一个序列中的数值代表身高,第二个序列中的数值代表体重。
3.3. 输出描述
排列结果,每个数值都是原始序列中的学生编号,编号从1开始
3.4. 用例
3.5. 题目解析
多条件排序,sort大法
3.6. 代码
class OrderByHeightWeight:
def __call__(self, heights: List[int], weights: List[int]):
students = [(height, weight, i) for i, (height, weight) in enumerate(zip(heights, weights), start=1)]
students.sort()
return list(map(lambda x: x[-1], students))
4. 报数问题
4.1. 题目描述
有n个人围成一圈,顺序排号为1-n。
从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
4.2. 输入描述
输入人数n(n < 1000)
4.3. 输出描述
输出最后留下的是原来第几号
4.4. 用例
4.5. 题目解析
本题是经典的约瑟夫环问题,最佳解题策略是利用循环链表。
因此,本题的关键是实现循环链表。
简易的循环链表实现:
-
循环链表节点定义
节点双向性prev、next(方便节点删除)
节点值val -
循环链表的属性:
链表长度size
链表头节点head
链表尾节点tail -
循环链表的操作
尾增节点(append操作)
删除任意节点(remove操作) -
循环链表尾增节点,需要注意:
如果size>0,则相当于只需要更新循环链表的尾节点tail
如果size == 0,则相当于更新循环链表的head和tail -
循环链表删除任意节点,需要注意:
如果删除的不是head,tail节点,则只需要将被删除节点的prev和next节点关联
如果删除的是head或tail,则还需要更新head、tail指向
4.6. 代码
class Node:
def __init__(self, val: int):
self.id = val
self.next = None
self.prev = None
class CycleLinkedList:
def __init__(self):
self.size = 0
self.head = None
self.tail = None
def append(self, node: int):
node = Node(node)
if self.size == 0:
self.head = node
self.tail = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node
self.tail.next = self.head
self.head.prev = self.tail
self.size += 1
def remove(self, node: Node):
prev, next = node.prev, node.next
prev.next = next
next.prev = prev
node.prev = node.next = None
if self.head == node:
self.head = next
if self.tail == node:
self.tail = prev
self.size -= 1
return next
class Remove3:
def __call__(self, n):
circle = CycleLinkedList()
for i in range(n):
if (i + 1) % 3 == 0:
continue
circle.append(i + 1)
cnt = 0
node = circle.head
while circle.size > 1:
cnt += 1
if cnt == 3:
node = circle.remove(node)
cnt = 0
else:
node = node.next
return node.id