2026 · SUMMER LEARNING LOG

每天学懂一点,
把思考留下来。

这里不只记录“做了什么”,还记录为什么这样想、哪里容易错,以及下一次准备挑战什么。

JUL 03

今天的关键词

左边 × −1 中间不变 右边 × −1
CF33C 符号翻转 最大子段和
03记录天数
24知识点
01待研究题目

成长不是把页面填满,
而是让一个想法真正变清楚。

LEARNING TIMELINE

这两天,我们走到了这里

从“为什么贪心是对的”,走到“怎样用最便宜的边连接所有点”。

03JUL

CF33C:从两次翻转走到最大子段和

从“覆盖两次不变”出发,自己推出“两头翻转、中间不变”,再把最终总和写成 2M − S。

  • 符号翻转
  • 代数转化
  • Kadane
打开今天的记录
02JUL

Kruskal 之后,认识 Cut 定理与 Prim

从“选边”切换到“长出一棵树”,并尝试用两个村庄修路的故事讲清楚 Cut 定理。

  • Kruskal
  • Cut 定理
  • Prim
打开 7 月 2 日记录
01JUL

交换一下,为什么不会更差?

学习交换论证,练习区间调度、最大迟到时间与奶牛玩杂技,开始把“感觉正确”写成证明。

  • 贪心
  • 交换论证
  • 调度
打开 7 月 1 日记录
2026 / 07 02 星期四

TODAY'S NOTE · 图论

最小生成树:
从选边,到长出一棵树

今天不是多记一个模板,而是找到 Prim 和 Kruskal 背后共同的理由。

01

已经会了

Kruskal

把边按权值排序,不断选择不会成环的最短边。

02

今天想清楚

Cut 定理

把点分成两边,跨过分界线的最便宜边是安全选择。

03

接下来掌握

Prim

从一个点出发,每次接入距离当前树最近的新点。

FOUNDATION · 基础地图

先把“最小生成树”说准确

算法模板之前,先分清图、树、生成树与最小生成树。

01

使用条件

研究对象通常是一个带权、无向、连通图。如果原图不连通,就不存在覆盖所有顶点的生成树。

02

什么是生成树

包含原图全部顶点、保持连通且没有环。若有 n 个顶点,生成树一定恰好有 n − 1 条边。

03

“最小”在哪里

比较的是整棵生成树的边权总和,不是让每一条边都分别最小,也不是求两个点之间的最短路。

04

答案可能不唯一

不同的生成树可能拥有相同的最小总权值。因此“最小生成树”不一定只有一棵,但最小权值相同。

CUT PROPERTY · 正确性核心

Cut 定理不只是口诀,
它解释了为什么可以贪心。

Cut(割)
把所有顶点分成两个非空集合。
跨割边
两个端点分别位于 Cut 两侧的边。
轻边
所有跨割边中权值最小的边。
安全边
加入当前选择后,仍然能够扩展成某棵最小生成树的边。

交换证明 · 5 步

  1. 1

    取一棵最小生成树 T,假设它没有选择轻边 e

  2. 2

    e 加入 T,树中会出现一个环。

  3. 3

    这个环上一定还有一条跨过同一 Cut 的边 f

  4. 4

    因为 e 是轻边,所以 w(e) ≤ w(f)

  5. 5

    删掉 f,仍是生成树且总权值不会变大。

所以:一定存在一棵最小生成树包含轻边 e

若轻边唯一,它属于所有最小生成树;若有并列轻边,只能保证任选一条可以进入某棵最小生成树。

01

核心概念

给孩子讲 Cut 定理

画一条线,把村庄分成左右两边。反正两边最终必须连起来,那就可以放心选择最便宜的跨线道路。

A B 已经连好的村庄
CUT
C D 还没连进来的村庄
画条线,分两边;要连通,必跨线;跨线道路选最短,放心加入不会亏。
02

方法对照

Kruskal 与 Prim

比较KruskalPrim
视角全图选边从树向外扩展
核心工具排序 + 并查集dis + vis
更适合稀疏图稠密图

Prim 最容易和 Dijkstra 混淆的地方:

// Prim:连接到当前树的边
dis[v] = min(dis[v], w);

// Dijkstra:从起点走到 v 的路
dis[v] = min(dis[v], dis[u] + w);
03

PRIM · 算法流程

让一棵树从一个点慢慢长大

  1. 初始化

    任选起点,例如 1 号点。令 dis[1] = 0,其余为无穷大。

  2. 找最近点

    在尚未加入生成树的点中,找到 dis 最小的点 u

  3. 加入答案

    标记 vis[u] = true,并把 dis[u] 加入生成树总权值。

  4. 更新距离

    枚举 u 的出边,用边权更新其他点:dis[v] = min(dis[v], w)

  5. 检查连通

    如果某轮找不到有限距离的新点,说明原图不连通,不存在最小生成树。

BORŮVKA · 第三种经典算法

思想一听就懂,
实现需要再跨一步。

当前状态 懂思想 · 不会实现 这是非常正常的学习阶段

每一轮,让每个连通块同时选择一条通向外部的最便宜边,再用并查集合并;不断重复,直到只剩一个连通块。

01

找到连通块

最开始每个点各自是一个连通块,以后由并查集维护。

02

寻找最便宜出边

扫描所有边,为每个连通块记录一条连接到外部的最小边。

03

统一合并

再次检查端点是否已经连通;若没有,就加入答案并执行 union。

04

进入下一轮

连通块数量至少大幅减少,重复约 log n 轮后完成。

为什么正确?

每个连通块都天然形成一个 Cut。

把这个连通块放在 Cut 一侧,其余顶点放在另一侧。它选择的最便宜出边就是跨过这个 Cut 的轻边,因此根据 Cut 定理,这条边是安全边。

代码真正难在哪里?

  • 每轮都要清空并重新记录各连通块的最便宜出边。
  • 一条边可能同时成为两个连通块的选择,不能重复计入。
  • 前面的合并会改变连通关系,加入边前必须再次 find。
  • 需要统计合并次数,判断已经连通还是原图不连通。
推荐学习顺序 Kruskal 与并查集熟练 手动模拟 Borůvka 理解每块的 cheapest[] 最后独立实现

WATCH OUT · 易错点

会写模板之前,先避开这些坑

  • 把 Prim 当成 Dijkstra:Prim 更新边权 w,不是路径长度 dis[u] + w
  • 忘记无向边:邻接矩阵或邻接表需要同时加入 u → vv → u
  • 重边直接覆盖:两个点之间有多条边时,应保留权值最小的一条。
  • 没有判断不连通:最小的未访问点仍是无穷大时,应报告不存在生成树。
  • 误解 Cut 定理:并列最小边不一定全部出现在同一棵最小生成树中。

QUICK CHECK

三问,看看是否真的明白

先在心里回答,再点开答案。

Cut 定理里的“Cut”到底是什么?

把所有顶点分成两个非空集合。端点分别落在两边的边,就是跨过这个 Cut 的边。

跨过 Cut 的最小边一定属于所有最小生成树吗?

不一定。它一定可以出现在某棵最小生成树中;只有当最小边唯一时,才能说它出现在所有最小生成树中。

Prim 的 dis[v] 表示什么?

点 v 通过一条边连接到“当前生成树”的最小代价,不是起点到 v 的路径长度。

一棵生成树为什么一定有 n − 1 条边?

树既连通又无环。从一个点开始,每加入一个新点恰好需要一条边,因此连接 n 个点需要 n − 1 条边。

Prim 从不同起点出发,答案会变吗?

最小总权值不会变,但当最小生成树不唯一时,最终选出的具体边集合可能不同。

Borůvka 为什么每一轮都要重新寻找最便宜出边?

合并后连通块已经改变。上一轮的出边可能变成连通块内部的边,因此必须根据新的连通块重新扫描和选择。

PARKING LOT · 暂存区

两道题,先放在这里

还没有正式研究,所以现在只收好题目、标记来源,不提前写思路和答案。等真正开始做时,再把过程补回来。

待研究

CODEFORCES 33C

Wonderful Randomized Sum

前缀、后缀与符号变化。先保留问题,暂不贴算法标签。

在洛谷查看原题
待研究

CODEFORCES 333B

Chips

棋盘、禁用格与移动冲突。先读懂规则,之后再寻找结构。

在洛谷查看原题
2026 / 07 01 星期三

YESTERDAY'S NOTE · 贪心

交换一下,
为什么不会更差?

第一次认真面对贪心证明:不是“它看起来最好”,而是说明它一定可以通向最优解。

1

拿一个最优解
先承认答案就在那里。

2

做一次交换
把它改得更像贪心选择。

3

证明不会更差
最优性没有被交换破坏。

GREEDY FOUNDATION · 基础

贪心算法到底在“贪”什么?

每一步做一个眼前最合适且不可撤销的选择,希望这些局部选择最终构成全局最优解。

01

贪心选择

当前只根据已经看到的信息做决定,不枚举所有完整方案,也通常不会回头修改。

02

安全策略

需要证明:至少存在一个最优解与这次贪心选择一致。看起来合理还不够。

03

最优子结构

做完一次安全选择后,剩余部分仍然是同类问题的一个更小实例。

04

举例不是证明

样例只能说明算法在几个输入上成功;证明必须覆盖所有满足条件的输入。

CASE 01

区间调度

结束得越早,给后面留下的空间越多。

按结束时间排序
  • 目标:选择最多个互不重叠区间
  • 安全选择:当前可选区间中结束最早者
  • 交换理由:替换最优解第一个区间后,不会挤占更多后续空间
CASE 02

最大迟到时间

截止时间早的任务,不应该被晚截止的任务挡在后面。

EDF:早截止先做
  • 目标:让所有任务中的最大迟到量最小
  • 逆序:前面任务截止时间反而更晚
  • 交换理由:消除相邻逆序不会增大最大迟到量
CASE 03

奶牛玩杂技

比较相邻两头奶牛交换前后的最坏风险。

按 w + s 排序
  • 目标:最小化最大的压扁指数
  • 只比较相邻两头奶牛的两种顺序
  • 交换理由:错误顺序调整后,其他奶牛受到的重量不变

PROOF WORKSHOP · 证明工具箱

交换论证的完整写法

  1. 选择对象

    设贪心算法当前选择的是 G,再任取一个最优解 S

  2. 处理一致情况

    如果 S 已经选择了 G,这一步天然安全,可以继续研究剩余问题。

  3. 找到可交换对象

    如果不一致,在 S 中找到与 G 对应的对象 X

  4. 执行替换

    G 替换 X,得到新方案 S′

  5. 证明两件事

    S′ 仍然合法,并且目标值不比 S 更差。

  6. 推进到完整方案

    不断交换,最终把某个最优解变成与贪心算法完全一致的方案。

COMMON ERRORS · 常见错误

证明最容易缺的五句话

  • 只描述了算法,没有证明为什么正确。
  • 只说“这样最好”,没有构造交换后的方案。
  • 只比较被交换的一个对象,忘了检查另一个对象。
  • 没有说明交换后其他对象为什么不受影响。
  • 证明了一次交换,却没说明如何重复到完整贪心顺序。

NEW PROBLEM · 遇到新题

寻找贪心规则的顺序

  1. 先把目标函数说清楚:最大什么,或最小什么?
  2. 列出几个自然策略:最早、最短、最大、最小、比值排序。
  3. 主动构造反例,尽早淘汰错误策略。
  4. 观察最优解和候选策略第一次不同的位置。
  5. 尝试局部交换,并检查合法性与目标值。

LEARNING KIT

昨天留下的完整学习包

讲义负责把话说清楚,互动课堂负责亲手试一试,综合训练负责检查是否真的会了。

ABOUT THIS LOG

这不是成绩单,
是一张思考生长的地图。

每条记录只回答三件事:今天弄懂了什么、还在哪里卡住、下一步准备往哪里走。

“待研究”不是空白,而是给未来的自己留下一个清楚的入口。