DSA图论:如何编程将n个点划分为N组使组内点尽可能接近?
一维点最优分组的展示方案
首先明确核心前提:因为所有点都在x轴上,最优分组必然是连续的区间(非连续分组的误差一定大于调整为连续后的误差),所以先对所有点按从小到大排序是关键步骤。
步骤1:基于动态规划的最优分组回溯
假设你已经实现了计算区间[start, end](点的索引,0/1起始均可)组内误差的函数calculate_error(start, end),接下来通过动态规划找到最优分组后,回溯出具体的分组边界:
动态规划定义
- 设
sorted_points为排序后的点列表,长度为n - 定义
dp[i][k]:前i个点分成k组的最小总误差 - 初始化:
dp[i][1] = calculate_error(0, i-1)(前i个点分成1组的误差) - 状态转移:
(分成k组的话,前j个点至少要分成k-1组,所以j≥k-1)dp[i][k] = min(dp[j][k-1] + calculate_error(j, i-1)) ,其中 j 从 k-1 到 i-1
回溯分组边界
从dp[n][N]倒推:
- 初始化
current = n,groups = [] - 从
k = Ndownto 1:- 遍历
j从k-1到current-1,找到使得dp[current][k] == dp[j][k-1] + calculate_error(j, current-1)的j值 - 将
sorted_points[j:current]加入groups - 令
current = j
- 遍历
- 反转
groups得到从左到右的分组顺序
步骤2:分组展示方案
文本展示(直接明了)
把每个组的点、组内均值、组内误差一起输出,示例代码:
# 假设回溯得到的groups是[[1.1, 2.2, 3.1], [5.3, 6.2, 7.0], [9.5, 10.1]] for idx, group in enumerate(groups, 1): group_mean = sum(group) / len(group) # 用你实现的函数计算当前组误差,这里假设函数接受起始/结束索引 start_idx = sum(len(g) for g in groups[:idx-1]) end_idx = start_idx + len(group) - 1 group_err = calculate_error(start_idx, end_idx) print(f"Group {idx}:") print(f" Points: {group}") print(f" Mean: {group_mean:.2f}, Error: {group_err:.2f}")
输出效果:
Group 1: Points: [1.1, 2.2, 3.1] Mean: 2.13, Error: 0.85 Group 2: Points: [5.3, 6.2, 7.0] Mean: 6.17, Error: 0.61 Group 3: Points: [9.5, 10.1] Mean: 9.80, Error: 0.18
可视化展示(ASCII版,快速直观)
利用x轴刻度标记点和分组,示例代码:
sorted_points = sorted(your_point_list) max_val = max(sorted_points) min_val = min(sorted_points) # 生成21个刻度点,覆盖所有数据范围 scale = [round(min_val + i*(max_val - min_val)/20, 1) for i in range(21)] print("X-axis scale: " + " ".join([f"{s:>4}" for s in scale])) print(" " + "----"*20) for idx, group in enumerate(groups, 1): line = [" "] * 21 for point in group: # 找到点对应的刻度位置 pos = min(range(21), key=lambda x: abs(scale[x] - point)) line[pos] = f"G{idx} " print(" " + "".join(line))
输出效果:
X-axis scale: 1.1 1.5 1.9 2.3 2.7 3.1 3.5 3.9 4.3 4.7 5.1 5.5 5.9 6.3 6.7 7.1 7.5 7.9 8.3 8.7 9.1 ---------------------------------------------------------------------------------------- G1 G1 G1 G2 G2 G2 G3 G3
关键注意事项
- 必须先对所有点排序,否则回溯得到的分组会混乱,不符合“接近程度”的分组逻辑
- 如果
n < N,直接每个点单独成组即可,无需计算 - 动态规划的索引要和你的
calculate_error函数的索引规则保持一致(0-based还是1-based)
内容的提问来源于stack exchange,提问作者Algoriphobia
相关产品推荐
相关产品推荐

