我上传了从欧拉计划翻译网站https://pe-cn.github.io/701/截取的中文图像

编写解决附件中题目的python程序
这是一个经典的概率期望问题,可以通过动态规划(Dynamic Programming)结合轮廓线(Broken Profile / Plug DP)来解决。由于网格较小 (7×77 \times 77×7),但状态空间较大,我们需要精心设计状态。
算法思路
- 目标 : 计算所有 2492^{49}249 种染色方案中,最大连通区域面积的总和,然后除以 2492^{49}249 得到期望。
- 状态定义 : 我们逐格处理网格。对于每个格子 (r,c)(r, c)(r,c),我们需要记录当前"轮廓线"上的连通性状态。
mask: 一个元组,表示当前轮廓线上每个位置所属的连通块 ID。sizes: 一个元组,记录每个连通块 ID 当前的面积(黑色格子数)。- 为了合并相同状态,我们需要对
mask进行最小表示法标准化(例如,第一个出现的连通块标记为 1,第二个为 2...)。
- DP 值 : 对于每个状态
(mask, sizes),我们需要记录一个直方图(字典),hist[k] = count,表示到达该状态且已封闭连通块的最大面积 为 kkk 的路径数量。- 为什么需要记录
max_closed?因为当我们在某处放置白色格子时,可能会封闭一个连通块。这个封闭块的大小可能成为最终的最大连通块面积。
- 为什么需要记录
- 转移 :
- 染白色 : 当前格子变为白色。检查上方格子所属的连通块是否被封闭(即在轮廓线其他位置不再出现)。如果封闭,更新
max_closed。 - 染黑色 : 当前格子变为黑色。尝试与上方 (
up) 和左方 (left) 的连通块合并。- 如果
up和left都存在且不同,合并它们,新面积 =size[up] + size[left] + 1。 - 如果只有一个存在,继承它,面积 +1。
- 如果都不存在,创建新连通块,面积 = 1。
- 注意检查合并后的面积(虽然题目没限制最大面积,但我们需要记录它)。
- 如果
- 染白色 : 当前格子变为白色。检查上方格子所属的连通块是否被封闭(即在轮廓线其他位置不再出现)。如果封闭,更新
- 结果计算 : 处理完所有格子后,遍历所有最终状态。对于每个状态,计算剩余活跃连通块的最大面积
max_active。最终该状态对总和的贡献是 ∑k(count×max(k,max_active))\sum_{k} (\text{count} \times \max(k, \text{max\_active}))∑k(count×max(k,max_active))。
Python 代码实现
python
import sys
# 增加递归深度,虽然主要使用迭代
sys.setrecursionlimit(5000)
def solve(W, H):
# 状态: (mask_tuple, sizes_tuple) -> { max_closed_area: count }
# mask_tuple: 长度为 W 的元组,表示轮廓线上每列的连通块 ID (0 表示白色/无)
# sizes_tuple: 长度为 K 的元组,sizes[i] 表示 ID 为 i 的连通块当前的面积
# 使用字典存储状态及其对应的 max_closed 分布
# 初始状态: 全 0
initial_mask = tuple([0] * W)
initial_sizes = tuple([0]) # ID 0 的大小为 0,ID 从 1 开始,动态扩展
# current_states 存储所有可能的状态
# key: (mask, sizes), value: dict {max_closed: count}
current_states = {
(initial_mask, initial_sizes): {0: 1}
}
for r in range(H):
for c in range(W):
next_states = {}
for (mask, sizes), hist in current_states.items():
# 当前格子 (r, c)
# 上方格子对应的 ID (在 mask 的索引 c 处)
up_id = mask[c]
# 左方格子对应的 ID (在 mask 的索引 c-1 处)
left_id = mask[c-1] if c > 0 else 0
# --- 选项 1: 染白色 ---
# 染白色意味着当前格子不属于任何黑色连通块
# mask[c] 变为 0
new_mask_list = list(mask)
new_mask_list[c] = 0
# 检查上方连通块是否封闭
closed_size = 0
if up_id != 0:
# 如果 up_id 不再出现在 new_mask_list 中,说明该连通块封闭
if up_id not in new_mask_list:
# 获取该 ID 的大小
if up_id < len(sizes):
closed_size = sizes[up_id]
else:
closed_size = 0 # 理论上不会发生
temp_mask = tuple(new_mask_list)
temp_sizes = sizes
# Normalize
norm_mask, norm_sizes = normalize(temp_mask, temp_sizes)
key = (norm_mask, norm_sizes)
if key not in next_states:
next_states[key] = {}
target_hist = next_states[key]
for old_mc, count in hist.items():
new_mc = old_mc if old_mc > closed_size else closed_size
target_hist[new_mc] = target_hist.get(new_mc, 0) + count
# --- 选项 2: 染黑色 ---
new_mask_list_black = list(mask)
# sizes 需要转为列表以便修改
temp_sizes_list = list(sizes)
if up_id == 0 and left_id == 0:
# 新连通块,分配新 ID
# 使用当前未使用的最小正整数,或者简单地用 len(sizes) (如果 sizes 紧凑)
# 由于 normalize 会重排,这里可以用任意未冲突 ID。
# 为了简单,用 max(mask) + 1,如果 mask 全 0 则用 1
# 但要注意 sizes 的索引。
# 我们直接用 len(temp_sizes_list) 作为新 ID,这样保证不越界且唯一
new_id = len(temp_sizes_list)
new_mask_list_black[c] = new_id
temp_sizes_list.append(1) # 新块大小为 1
elif up_id != 0 and left_id == 0:
# 继承上方
new_mask_list_black[c] = up_id
if up_id >= len(temp_sizes_list):
temp_sizes_list.extend([0] * (up_id - len(temp_sizes_list) + 1))
temp_sizes_list[up_id] += 1
elif up_id == 0 and left_id != 0:
# 继承左方
new_mask_list_black[c] = left_id
if left_id >= len(temp_sizes_list):
temp_sizes_list.extend([0] * (left_id - len(temp_sizes_list) + 1))
temp_sizes_list[left_id] += 1
else: # up_id != 0 and left_id != 0
if up_id == left_id:
# 同一个块
new_mask_list_black[c] = up_id
if up_id >= len(temp_sizes_list):
temp_sizes_list.extend([0] * (up_id - len(temp_sizes_list) + 1))
temp_sizes_list[up_id] += 1
else:
# 合并
main_id = min(up_id, left_id)
other_id = max(up_id, left_id)
new_mask_list_black[c] = main_id
# 确保列表足够长
if main_id >= len(temp_sizes_list):
temp_sizes_list.extend([0] * (main_id - len(temp_sizes_list) + 1))
if other_id >= len(temp_sizes_list):
temp_sizes_list.extend([0] * (other_id - len(temp_sizes_list) + 1))
temp_sizes_list[main_id] += temp_sizes_list[other_id] + 1
# 将 mask 中所有的 other_id 替换为 main_id
for i in range(W):
if new_mask_list_black[i] == other_id:
new_mask_list_black[i] = main_id
temp_mask_black = tuple(new_mask_list_black)
temp_sizes_black = tuple(temp_sizes_list)
norm_mask_b, norm_sizes_b = normalize(temp_mask_black, temp_sizes_black)
key_b = (norm_mask_b, norm_sizes_b)
if key_b not in next_states:
next_states[key_b] = {}
target_hist_b = next_states[key_b]
for old_mc, count in hist.items():
new_mc = old_mc
target_hist_b[new_mc] = target_hist_b.get(new_mc, 0) + count
current_states = next_states
# 可选:打印进度
# if r == H - 1 and c % 2 == 0:
# print(f"Processed ({r}, {c}), states: {len(current_states)}")
# 计算最终结果
total_sum_max_area = 0
for (mask, sizes), hist in current_states.items():
# 此时所有格子处理完毕。
# mask 中可能还有活跃的连通块 (最后一行)。
unique_ids = set(mask) - {0}
max_active = 0
for uid in unique_ids:
if uid < len(sizes):
if sizes[uid] > max_active:
max_active = sizes[uid]
for mc, count in hist.items():
final_max = mc if mc > max_active else max_active
total_sum_max_area += count * final_max
total_configs = 2 ** (W * H)
expected_value = total_sum_max_area / total_configs
return expected_value
def normalize(mask, sizes):
"""
将 mask 转换为最小表示法,并相应重排 sizes。
"""
mapping = {}
next_id = 1
new_mask_list = []
for x in mask:
if x == 0:
new_mask_list.append(0)
else:
if x not in mapping:
mapping[x] = next_id
next_id += 1
new_mask_list.append(mapping[x])
new_mask = tuple(new_mask_list)
# 重排 sizes
# ID 0 的大小恒为 0 (虽然 sizes[0] 可能无意义,我们保持它为 0)
# 新 ID k 对应旧 ID mapping^{-1}(k)
inv_mapping = {v: k for k, v in mapping.items()}
# 新 sizes 长度为 next_id (因为 ID 从 1 到 next_id-1, 加上 0)
new_sizes_list = [0] * next_id
for new_id, old_id in inv_mapping.items():
if old_id < len(sizes):
new_sizes_list[new_id] = sizes[old_id]
else:
new_sizes_list[new_id] = 0
new_sizes = tuple(new_sizes_list)
return new_mask, new_sizes
if __name__ == '__main__':
print("Calculating E(2, 2)...")
e_2_2 = solve(2, 2)
print(f"E(2, 2) = {e_2_2}")
print("Calculating E(4, 4)...")
e_4_4 = solve(4, 4)
print(f"E(4, 4) = {e_4_4:.8f}")
print("Calculating E(7, 7)...")
e_7_7 = solve(7, 7)
print(f"E(7, 7) = {e_7_7:.8f}")