Merge branch 'main' of git.gangary.cn:gary/maze_python

This commit is contained in:
Gary Gan 2025-06-30 16:33:54 +08:00
commit 120182ea68

View File

@ -1,136 +1,110 @@
#-*-coding: GBK -*-
import heapq # 导入堆模块,用于实现优先队列
from typing import List, Optional, Tuple # 导入类型提示相关模块
import math
import heapq
def boss_fight(B, PlayerSkills):
n = len(B)
m = len(PlayerSkills)
if n == 0:
return []
def boss_strategy(coin: int, blood: int) -> Optional[Tuple[List[str], int]]:
# 使用分支限界法寻找最小回合数击败BOSS的技能序列
# 计算最大平均伤害用于启发式函数
max_avg = 0.0
for a, b in PlayerSkills:
if b + 1 > 0:
avg = a / (b + 1)
if avg > max_avg:
max_avg = avg
# 如果所有技能伤害为0则无法击败BOSS
if max_avg == 0:
return None
# 参数:
# coin: 玩家初始金币数
# blood: BOSS初始血量
# 初始状态boss索引0血量B[0]冷却全0回合数0路径空
start_cooldown = tuple([0] * m)
total_remaining = sum(B)
#f_value = 当前回合数 + 估计剩余回合数
f_value = math.ceil(total_remaining / max_avg) if max_avg > 0 else total_remaining
# 返回:
# 最小回合数的技能序列列表如果无解则返回None
# 初始化优先队列 (f_value, round_count, boss_idx, boss_rem, cooldown, path)
queue = [(f_value, 0, 0, B[0], start_cooldown, [])]
# 记录访问过的状态(boss_idx, boss_rem, cooldown) -> 最小回合数
visited = {}
visited[(0, B[0], start_cooldown)] = 0
start_coin = coin # 记录初始金币数,用于计算消耗金币
all_path = []
# 优先队列按f_value排序
while queue:
# 优先扩展f_value最小的状态最有希望的状态
f_value, round_count, boss_idx, boss_rem, cooldown, path = heapq.heappop(queue)
# 初始状态: (回合数, 剩余金币, BOSS血量, 充能, 连续E计数, 技能序列元组)
start_state = (0, coin, blood, 0, 0, ())
state_key = (boss_idx, boss_rem, cooldown)
# 创建优先队列: 元素为 (优先级, 状态)
# 优先级元组: (回合数, 消耗金币, BOSS血量) - 值越小优先级越高
heap = []
heapq.heappush(heap, ((0, 0, blood), start_state)) # 将初始状态加入优先队列
# 创建访问集合: 用于避免重复状态
# 状态键: (回合数, 剩余金币, BOSS血量, 充能, min(连续E计数,3))
visited = set()
start_ce_compressed = min(0, 3) # 压缩连续E计数状态
visited.add((0, coin, blood, 0, start_ce_compressed)) # 将初始状态加入已访问集合
# 设置最大回合数限制,避免无限循环
max_round = blood * 2 # 每次至少造成1点伤害最多回合数为血量两倍
# 主循环: 处理优先队列中的状态
while heap:
# 从优先队列中取出优先级最高的状态
priority, state = heapq.heappop(heap)
# 解包状态元组
round_cnt, cur_coin, cur_blood, energy, ce, seq_tuple = state
# 检查是否已击败BOSS
if cur_blood <= 0:
return (list(seq_tuple), cur_coin) # 返回技能序列和剩余金币
# 超过最大回合数则跳过当前状态
if round_cnt >= max_round:
# 状态检查:如果已有更好状态,跳过当前状态
if visited.get(state_key, float('inf')) < round_count:
continue
# 尝试使用技能A
if cur_coin >= 2: # 检查金币是否足够
new_coin = cur_coin - 2 # 扣除金币
new_blood = cur_blood - 1 # 减少BOSS血量
new_energy = energy + 1 # 增加充能
new_ce = 0 # 使用非E技能重置连续E计数
new_round = round_cnt + 1 # 回合数+1
new_seq = seq_tuple + ('A',) # 添加技能到序列
# 所有BOSS已击败
if boss_idx >= n:
return path
# 压缩连续E计数状态>=3视为3
ce_compressed = min(new_ce, 3)
# 创建状态键用于去重
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
# 等待操作所有冷却时间减1回合数+1路径添加-1
new_cooldown_list = [max(0, c - 1) for c in cooldown]
new_cooldown = tuple(new_cooldown_list)
new_round = round_count + 1
new_state_key = (boss_idx, boss_rem, new_cooldown)
# 若新状态更优,则加入队列
if new_state_key not in visited or visited[new_state_key] > new_round:
visited[new_state_key] = new_round
total_remaining = boss_rem + sum(B[boss_idx+1:])
estimate_rounds = math.ceil(total_remaining / max_avg) if max_avg > 0 else 0
new_f = new_round + estimate_rounds
new_path = path + [-1]
heapq.heappush(queue, (new_f, new_round, boss_idx, boss_rem, new_cooldown, new_path))
# 检查是否为新状态
if state_key not in visited:
visited.add(state_key) # 标记为已访问
cost = start_coin - new_coin # 计算已消耗金币
# 创建新状态
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
# 将新状态加入优先队列
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
# 使用技能
for skill_idx, (a, b) in enumerate(PlayerSkills):
if cooldown[skill_idx] != 0: # 技能不可用
continue
# 尝试使用技能E
cost_e = 3 # 默认消耗金币
if ce >= 2: # 检查是否为连续第三次使用E
cost_e = 2 # 第三次消耗2金币
new_boss_idx = boss_idx
new_boss_rem = boss_rem - a
if cur_coin >= cost_e: # 检查金币是否足够
new_coin = cur_coin - cost_e # 扣除金币
new_blood = cur_blood - 3 # 减少BOSS血量
new_energy = energy + 1 # 增加充能
new_ce = ce + 1 # 增加连续E计数
new_round = round_cnt + 1 # 回合数+1
new_seq = seq_tuple + ('E',) # 添加技能到序列
# 压缩连续E计数状态
ce_compressed = min(new_ce, 3)
# 创建状态键用于去重
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
# 检查是否为新状态
if state_key not in visited:
visited.add(state_key) # 标记为已访问
cost = start_coin - new_coin # 计算已消耗金币
# 创建新状态
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
# 将新状态加入优先队列
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
# 尝试使用技能Q
if energy >= 3 and cur_coin >= 3: # 检查充能和金币是否足够
new_coin = cur_coin - 3 # 扣除金币
new_blood = cur_blood - 8 # 减少BOSS血量
new_energy = energy - 3 # 消耗充能
new_ce = 0 # 使用非E技能重置连续E计数
new_round = round_cnt + 1 # 回合数+1
new_seq = seq_tuple + ('Q',) # 添加技能到序列
# 压缩连续E计数状态
ce_compressed = min(new_ce, 3)
# 创建状态键用于去重
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
# 检查是否为新状态
if state_key not in visited:
visited.add(state_key) # 标记为已访问
cost = start_coin - new_coin # 计算已消耗金币
# 创建新状态
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
# 将新状态加入优先队列
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
return None # 队列为空,无解
# 主程序入口
if __name__ == "__main__":
# 测试示例: 金币=10, BOSS血量=10
result = boss_strategy(20, 20)
if result is None:
print("无法击败BOSS")
# 击败当前BOSS
if new_boss_rem <= 0:
new_boss_idx += 1
all_path.append(new_path)
new_path = []
if new_boss_idx < n:
new_boss_rem = B[new_boss_idx] # 切换至下一个BOSS
else:
skill_sequence, remaining_coins = result
print("最小回合数技能序列:", skill_sequence) # 预期输出: ['A', 'A', 'A', 'Q']
print("剩余金币:", remaining_coins) # 预期输出: 1
new_boss_rem = 0 # 所有BOSS已击败
# 更新冷却时间
new_cooldown_list = [max(0, c - 1) for c in cooldown]
new_cooldown_list[skill_idx] = b # 重置当前技能冷却时间
new_cooldown = tuple(new_cooldown_list)
new_round = round_count + 1
new_path = path + [skill_idx]
# 所有BOSS已击败返回路径
if new_boss_idx >= n:
return all_path
new_state_key = (new_boss_idx, new_boss_rem, new_cooldown)
# 若新状态更优,则加入队列
if new_state_key not in visited or visited[new_state_key] > new_round:
visited[new_state_key] = new_round
total_remaining = new_boss_rem + sum(B[new_boss_idx+1:])
estimate_rounds = math.ceil(total_remaining / max_avg) if max_avg > 0 else 0
new_f = new_round + estimate_rounds
heapq.heappush(queue, (new_f, new_round, new_boss_idx, new_boss_rem, new_cooldown, new_path))
return None # 无解
if __name__ == '__main__':
B = [10, 20, 30, 40, 50]
PlayerSkills = [(5, 2), (8, 3), (3, 1)]
ans = boss_fight(B, PlayerSkills)
for i in ans:
print(i)