02814: 拨钟问题
http://cs101.openjudge.cn/practice/02814/
有9个时钟,排成一个3*3的矩阵。
|-------| |-------| |-------|
| | | | | | |
|---O | |---O | | O |
| | | | | |
|-------| |-------| |-------|
A B C
|-------| |-------| |-------|
| | | | | |
| O | | O | | O |
| | | | | | | | |
|-------| |-------| |-------|
D E F
|-------| |-------| |-------|
| | | | | |
| O | | O---| | O |
| | | | | | | |
|-------| |-------| |-------|
G H I
(图 1)现在需要用最少的移动,将9个时钟的指针都拨到12点的位置。共允许有9种不同的移动。如下表所示,每个移动会将若干个时钟的指针沿顺时针方向拨动90度。
移动 影响的时钟
1 ABDE
2 ABC
3 BCEF
4 ADG
5 BDEFH
6 CFI
7 DEGH
8 GHI
9 EFHI输入
9个整数,表示各时钟指针的起始位置,相邻两个整数之间用单个空格隔开。其中,0=12点、1=3点、2=6点、3=9点。
输出
输出一个最短的移动序列,使得9个时钟的指针都指向12点。按照移动的序号从小到大输出结果。相邻两个整数之间用单个空格隔开。
样例输入
3 3 0
2 2 2
2 1 2样例输出
4 5 8 9来源:1166
这是一个经典的搜索或组合优化问题。由于每个移动(1-9号)执行 4 次后指针会回到原位,因此每个移动的次数只需在
以下是实现该逻辑的代码。我们使用嵌套循环遍历每种移动的组合,并利用早期剪枝(在循环中途检查部分时钟是否回零)来提高效率。
Python 代码实现
python
import sys
def solve():
# 读取输入的9个整数(时钟初始状态)
try:
input_data = sys.stdin.read().split()
if not input_data:
return
clocks = [int(x) for x in input_data]
except EOFError:
return
# 变量 m1, m2, ..., m9 分别代表移动 1-9 执行的次数 (0-3次)
# 根据题目给出的“移动影响的时钟”,我们可以推导出每个时钟受哪些移动的影响:
# A (index 0): 1, 2, 4
# B (index 1): 1, 2, 3, 5
# C (index 2): 2, 3, 6
# D (index 3): 1, 4, 5, 7
# E (index 4): 1, 3, 5, 7, 9
# F (index 5): 3, 5, 6, 9
# G (index 6): 4, 7, 8
# H (index 7): 5, 7, 8, 9
# I (index 8): 6, 8, 9
# 使用嵌套循环进行穷举。为了提速,我们在循环中间进行判断(剪枝)
for m1 in range(4):
for m2 in range(4):
for m3 in range(4):
for m4 in range(4):
# 检查时钟 A 是否能回到12点(0点)
if (clocks[0] + m1 + m2 + m4) % 4 != 0:
continue
for m5 in range(4):
# 检查时钟 B
if (clocks[1] + m1 + m2 + m3 + m5) % 4 != 0:
continue
for m6 in range(4):
# 检查时钟 C
if (clocks[2] + m2 + m3 + m6) % 4 != 0:
continue
for m7 in range(4):
# 检查时钟 D
if (clocks[3] + m1 + m4 + m5 + m7) % 4 != 0:
continue
for m8 in range(4):
# 检查时钟 G
if (clocks[6] + m4 + m7 + m8) % 4 != 0:
continue
for m9 in range(4):
# 检查剩余时钟 E, F, H, I
if (clocks[4] + m1 + m3 + m5 + m7 + m9) % 4 != 0: continue
if (clocks[5] + m3 + m5 + m6 + m9) % 4 != 0: continue
if (clocks[7] + m5 + m7 + m8 + m9) % 4 != 0: continue
if (clocks[8] + m6 + m8 + m9) % 4 != 0: continue
# 找到解,格式化输出
counts = [m1, m2, m3, m4, m5, m6, m7, m8, m9]
result = []
for i in range(9):
for _ in range(counts[i]):
result.append(str(i + 1))
print(" ".join(result))
return
if __name__ == "__main__":
solve()代码说明:
- 时钟状态:题目中
分别代表 点。每移动一次相当于状态加 。我们只需要让 (初始状态 + 总移动次数) % 4 == 0即可。 - 穷举与剪枝:虽然总共有
种情况,但通过在每一层循环后尽早判断对应的时钟是否已经归零,可以极大地减少不必要的计算。例如,一旦确定了 ,时钟 的最终状态就固定了,如果此时它不为 ,则不需要继续往下循环。 - 唯一性:在线性代数中,这个系统可以看作是一个在模4运算下的线性方程组。由于其转换矩阵是可逆的,所以在
范围内存在唯一的解,这组解对应的移动总数自然就是最短的。 - 输出:最后按照移动序号从小到大输出,满足题目要求。