题目解析
问题描述
小U在时尚圈组织了一场“校服日”活动,有 n 名学生围成一圈参加活动。每位学生都穿着白色或黑色的校服,白色用 0 表示,黑色用 1 表示。每一天,如果某个学生发现自己和相邻的两位学生穿着相同颜色的校服,那么他会在第二天更换成另一种颜色的校服;否则,他会保持不变。
你的任务是帮助小U判断,在第几天学生们的穿着会达到稳定状态——即某一天后,所有学生的穿着不再发生任何变化。同时,你还需要计算在稳定后,有多少学生的校服颜色与其相邻的同学不同,这些学生被称为“时尚达人”。
最终你需要返回一个包含两个元素的数组:
- 第一个数字表示稳定下来的天数。如果永远无法稳定,则输出
-1。 - 第二个数字表示稳定后成为“时尚达人”的学生人数。如果永远无法稳定,则输出
-1。
思路
-
初始状态:
- 将输入的字符串转换为列表,方便后续操作。
- 记录初始状态,并使用集合记录出现过的状态,以检测是否进入循环。
-
模拟每一天的变化:
- 遍历每个学生,检查他们是否需要更换校服颜色。
- 如果当前学生和相邻学生颜色相同,则更换颜色;否则保持不变。
-
检测稳定状态:
- 如果下一天的状态与当前状态相同,则说明状态已经稳定。
- 计算稳定后的时尚达人数量。
-
检测循环:
- 如果状态重复出现,说明进入循环,返回
[-1, -1]。
- 如果状态重复出现,说明进入循环,返回
-
最大步数限制:
- 理论上,超过
2n步数必定重复,设置一个最大步数限制以避免无限循环。
- 理论上,超过
图解
假设 n = 4,初始状态为 "1110":
- 第0天:
1110 - 第1天:
0001(所有学生都更换颜色) - 第2天:
1110(所有学生都更换颜色,回到初始状态)
此时状态重复,进入循环,返回 [-1, -1]。
代码详解
# 将初始状态保存
current_state = list(map(int, data))
seen_states = set() # 记录出现过的状态
days = 0 # 记录天数
while True:
# 将当前状态加入已见状态集合
state_tuple = tuple(current_state)
if state_tuple in seen_states:
return [-1, -1] # 状态重复,进入循环
seen_states.add(state_tuple)
days += 1
next_state = current_state[:]
# 遍历每个学生更新下一天的状态
for i in range(n):
# 获取左右邻居(环形处理)
left = current_state[(i - 1) % n]
right = current_state[(i + 1) % n]
# 判断是否需要改变颜色
if current_state[i] == left == right:
next_state[i] = 1 - current_state[i] # 改变颜色
# 判断是否稳定
if next_state == current_state:
# 稳定后计算时尚达人数量
fashionistas = 0
for i in range(n):
left = next_state[(i - 1) % n]
right = next_state[(i + 1) % n]
if next_state[i] != left or next_state[i] != right:
fashionistas += 1
return [days, fashionistas]
# 更新状态
current_state = next_state
# 设置一个最大步数限制(理论上,超过 2n 必定重复)
if days > 2 * n:
return [-1, -1]
if __name__ == "__main__":
# Add your test cases here
print(solution(4, "0000") == [-1, -1])
print(solution(4, "1110") == [2, 4])
print(solution(6, "010101") == [1, 6])
总结
- 状态模拟:通过模拟每一天的状态变化,逐步逼近稳定状态。
- 循环检测:使用集合记录出现过的状态,检测是否进入循环。
- 稳定检测:通过比较当前状态和下一天的状态,判断是否达到稳定。
- 时尚达人计算:在稳定后,计算与相邻学生颜色不同的学生数量。
感悟
这道题目通过模拟学生校服颜色的变化,考察了对状态变化的模拟和循环检测的能力。通过逐步模拟每一天的状态变化,我们可以有效地判断状态是否稳定,并计算出稳定后的时尚达人数量。这道题目不仅考察了编程能力,还考察了对问题本质的理解和逻辑推理能力。