从曼哈顿距离到BFS:蓝桥杯“扩散”题的数学建模与算法优化 📅 发布时间:2026/8/28 7:59:25 👁 浏览次数: 1. 从“扩散”到“广度优先”一道国赛题的思维跃迁看到“扩散”这个词很多人的第一反应可能是物理现象或者图像处理。但在2020年蓝桥杯国赛C B组的赛场上它被赋予了一个全新的、充满算法趣味的定义。这道题没有冗长的背景故事题干本身可能只有寥寥数语但它精准地考察了选手将实际问题抽象为图论模型并运用广度优先搜索BFS这一基础算法高效求解的能力。这恰恰是算法竞赛的魅力所在——用简洁的规则构建复杂的逻辑世界。这道题的核心可以这样理解在一个无限的二维网格平面上最初有四个点被“感染”或称为“黑点”。每一分钟每个被感染的点会向其上、下、左、右四个相邻的网格点扩散感染。问题通常会是在第2020分钟或者某个特定的时间T结束时有多少个网格点被感染了初看之下这似乎是一个模拟题直接开一个足够大的数组每分钟更新状态不就行了但“无限平面”和“2020分钟”这两个条件结合在一起立刻排除了暴力模拟的可行性。2020分钟的扩散最远影响距离可能达到2020*48080个网格单位如果四个点朝一个方向扩散这意味着模拟区域边长可能超过16000数组大小将是一个天文数字无论是时间还是空间复杂度都无法承受。因此这道题的关键在于发现规律、建立模型、优化求解。它要求选手跳出“模拟每一步”的惯性思维转而思考感染传播的本质——距离。2. 问题本质剖析曼哈顿距离与感染区域要高效解决这个问题我们首先要进行数学抽象。将初始的四个感染点视为平面上的四个源点。根据规则感染每分钟向四邻域扩散一步。那么一个网格点(x, y)在t时刻被感染的条件是什么它意味着存在一个初始源点(x0, y0)从该源点走到(x, y)所需的步数即时间小于等于t。并且由于每分钟只能走一步上、下、左、右这个步数就是两点之间的曼哈顿距离Manhattan Distance。曼哈顿距离公式为d |x - x0| |y - y0|。所以(x, y)在t时刻被感染的条件是存在一个初始源点使得该点到(x, y)的曼哈顿距离d t。这样一来问题发生了根本性的转变我们不需要模拟时间流逝只需要计算在曼哈顿距离t范围内能被至少一个源点覆盖的所有整数坐标点(x, y)的个数。这变成了一个计算几何问题更具体地说是计算多个菱形曼哈顿距离下的“圆”的并集所包含的整数点个数。每个源点(x0, y0)在曼哈顿距离t下的覆盖区域是一个中心在(x0, y0)、对角线竖直水平的菱形或者说是一个旋转了45度的正方形。2.1 从无限平面到有限区域确定搜索边界虽然平面是无限的但我们需要计算的只是距离四个源点t步以内的点。因此我们可以轻松确定一个有限的矩形区域作为搜索范围。设四个初始点的坐标分别为(xi, yi)其中i1,2,3,4。 计算所有点的最小横坐标min_x最大横坐标max_x最小纵坐标min_y最大纵坐标max_y。 那么在t时刻所有可能被感染的点一定落在以下矩形区域内x 的范围[min_x - t, max_x t] y 的范围[min_y - t, max_y t]这个区域的大小是有限的边长大约在(max_x - min_x 2*t 1)和(max_y - min_y 2*t 1)这个量级。对于t2020和初始点坐标范围不大的情况这个区域是完全可以遍历的。2.2 暴力枚举法的可行性分析基于以上分析最直接的算法浮出水面双重循环遍历上述矩形区域内的每一个整数点(x, y)对于每个点计算其到四个源点的曼哈顿距离如果有一个距离 t则该点被感染计数器加一。我们来估算一下计算量。假设初始点坐标绝对值在100以内t2020那么矩形区域边长大约为1002020*2 ≈ 4140。总点数约为4140 * 4140 ≈ 17, 139, 600即1700万量级。对于每个点需要计算4次曼哈顿距离4次绝对值加法比较。总操作次数约为1700万 * 4 6800万次基本运算。在现代计算机上C完成6800万次简单运算是完全可行的通常在1秒以内。因此暴力枚举法对于本题的规模是切实可行的。这也是本题被归为B组题目的原因之一它鼓励选手先找到正确的数学模型然后采用直观且有效的方法实现。注意这里体现了算法竞赛中的一个重要思维——复杂度估算。在动手前先对问题规模和处理器的能力做一个粗略估计能避免走入死胡同也能为优化提供方向。3. 算法实现细节与BFS的对照虽然暴力枚举足够解题但题目名称中的“扩散”很容易让人联想到BFS。我们不妨也探讨一下BFS的思路并对比其优劣这能加深对问题本质的理解。3.1 广度优先搜索BFS思路BFS模拟了感染扩散的实时过程将四个初始点加入队列并标记为已访问。每次从队列中取出一个点(x, y)将其步数时间记为step。如果step t则遍历其四个邻居(nx, ny)。如果邻居未被访问过则将其步数设为step1标记为已访问并加入队列。重复步骤2-4直到队列为空。统计所有被标记访问过的点的数量即为答案。BFS的潜在问题空间开销大我们需要记录每个点是否被访问过。由于点可能分布在一个很大的矩形区域我们需要使用一个高效的判重数据结构。使用STL的std::set或std::unordered_set来存储点的坐标例如pairint, int在插入和查询时会有较大的时间开销对数或平均常数时间但常数较大。当感染点数量达到百万级时这个开销可能变得显著。时间开销BFS需要显式地探索每一个被感染的点及其邻居。最终被感染的点数量就是我们需要统计的答案假设为N。那么BFS的过程本身就需要进行大约N * 4次的邻居检查和队列操作。对于t2020的情况N本身可能就在千万量级这导致BFS的总操作次数与暴力枚举法处于同一量级甚至更多且每次操作集合查找、插入的成本更高。3.2 暴力枚举法的实现要点相比之下暴力枚举法实现更简单且通常更快。以下是C实现的核心步骤定义初始点与时间T// 假设初始点坐标根据题目具体给出这里是示例 int points[4][2] {{0, 0}, {2020, 11}, {11, 14}, {2000, 2000}}; int T 2020;确定搜索边界int min_x points[0][0], max_x points[0][0]; int min_y points[0][1], max_y points[0][1]; for (int i 1; i 4; i) { min_x min(min_x, points[i][0]); max_x max(max_x, points[i][0]); min_y min(min_y, points[i][1]); max_y max(max_y, points[i][1]); } int start_x min_x - T; int end_x max_x T; int start_y min_y - T; int end_y max_y T;双重循环遍历与判断long long ans 0; // 使用long long防止溢出 for (int x start_x; x end_x; x) { for (int y start_y; y end_y; y) { bool infected false; for (int i 0; i 4; i) { int dx abs(x - points[i][0]); int dy abs(y - points[i][1]); if (dx dy T) { infected true; break; // 找到一个可达源点即可 } } if (infected) { ans; } } } cout ans endl;为什么暴力枚举在此处更优内存访问连续双重循环遍历一个虚拟的矩形区域内存访问模式是连续且可预测的对CPU缓存友好。计算极其简单核心计算是整数加减、绝对值和比较现代CPU可以在一个时钟周期内完成多条这样的指令。无额外数据结构开销不需要维护队列、集合等动态数据结构避免了内存分配和复杂查找的开销。实操心得在算法竞赛中最简单的办法往往就是最好的办法前提是你能通过复杂度分析确认它不会超时。不要因为题目叫“扩散”就非得用BFS。先建模再选算法。4. 优化进阶从O(N²)到O(N)的思维暴力枚举法的时间复杂度是O(W * H * 4)其中W和H是搜索区域的宽度和高度。这本质上与感染面积点数N乘以一个常数成正比可以认为是O(N)的因为需要遍历可能区域内的每个点进行检查。但有没有可能不遍历这么多点直接计算出答案呢答案是肯定的这需要更深入的数学工具。我们可以把问题转化为求四个菱形的并集面积整数点个数。求多个凸多边形的并集面积有标准的算法如扫描线算法。4.1 扫描线算法思路简介对于每个源点产生的菱形我们可以求出它在每一行y坐标固定上覆盖的x坐标范围。因为菱形是中心对称的对于源点(x0, y0)和距离t在纵坐标y这一行能被覆盖的横坐标x需要满足|x - x0| |y - y0| t即|x - x0| t - |y - y0|令remain t - |y - y0|如果remain 0则这一行没有任何点被该源点覆盖。 否则覆盖的x区间为[x0 - remain, x0 remain]。这样对于每一个y我们都能得到来自四个源点的最多四个区间。问题就变成了对于固定的y求这四个区间的并集长度然后将所有y对应的并集长度累加。求多个区间的并集长度是一个经典问题可以通过区间合并算法在O(m log m)内解决m是区间数这里最大为4。4.2 扫描线算法实现框架确定y的扫描范围[min_y - t, max_y t]。对范围内的每一个整数y a. 初始化一个空的区间向量intervals。 b. 对于每个源点(x0, y0)计算remain t - abs(y - y0)。 c. 如果remain 0则将区间[x0 - remain, x0 remain]加入intervals。 d. 对intervals按左端点排序然后进行区间合并计算出这些区间在x轴上的总覆盖长度len。 e. 将len累加到答案ans中。输出ans。复杂度分析y的扫描范围大小约为O(t)对于每个y进行4次计算和一次最多4个区间的合并O(1)。总复杂度为O(t)这比暴力枚举的O(t²)有显著提升。当t很大时比如t10^9这种方法的优势是决定性的。代码片段示例区间合并部分// intervals 是一个 vectorpairint, int 存储了 [l, r] 区间 sort(intervals.begin(), intervals.end()); long long total_len 0; int cur_l intervals[0].first, cur_r intervals[0].second; for (int i 1; i intervals.size(); i) { if (intervals[i].first cur_r 1) { // 注意曼哈顿距离覆盖的是整数点区间[1,2]和[3,4]是连续的因为点2和点3相邻。 cur_r max(cur_r, intervals[i].second); } else { total_len (cur_r - cur_l 1); cur_l intervals[i].first; cur_r intervals[i].second; } } total_len (cur_r - cur_l 1); ans total_len;关键细节在区间合并判断是否连续时条件应该是intervals[i].first cur_r 1而不是intervals[i].first cur_r。因为cur_r是上一个区间覆盖的最后一个整数点下一个区间如果从cur_r1开始这两个区间在整数点上是连续的。这是本题用扫描线算法时最容易出错的地方。5. 调试、验证与常见“坑点”无论采用哪种方法正确的实现都需要经过验证。以下是几个关键的验证点和常见错误5.1 边界与数据类型坐标范围初始点坐标和t可能为负数吗题目通常会给明确坐标。我们的搜索边界计算min_x - t要考虑到负数情况。整数溢出这是最大的“坑”。t2020时答案可能很大。假设每个源点独立扩散一个点覆盖的面积大约是2*t^2量级菱形面积近似。四个点即使有重叠答案也可能达到千万甚至上亿。因此用于计数的变量如ans必须使用long long64位整数。在C中int通常是32位最大值约21亿在本题数据规模下有可能溢出。循环边界在暴力枚举中for循环的边界是 end_x而不是 end_x务必确认。5.2 验证策略小数据测试用小的t如1, 2, 3手动计算或编写一个简单的BFS程序进行验证。比较暴力枚举、扫描线、BFS三种方法的结果是否一致。对称性检验如果初始点关于某条直线对称那么感染区域也应该对称。可以利用这个性质检查程序输出是否合理。时间增长验证观察t每增加1答案的增加量是否符合预期。感染区域的增长应该是有规律的。5.3 针对本题的特定陷阱初始点重合题目并未说明四个初始点是否互异。如果存在重合点在计算时它们应被视为同一个源点。但在我们的算法中无论是暴力枚举距离判断还是扫描线区间生成重合点都不会导致重复计数因为判断条件是“存在一个源点满足距离t”多个重合源点与一个源点的效果相同。不过在确定搜索边界时如果直接用重合点计算min_x, max_x等结果依然是正确的。时间起点明确“第2020分钟结束时”的含义。通常t0表示初始状态已有4个点被感染。那么t1时感染点会增加。我们的算法中“距离 t” 对应的是t时刻结束时包括初始时刻的状态。这一点需要与题目描述严格对照。6. 从解题到举一反三模型拓展与应用解完一道题价值在于其背后的模型和思想能否迁移。这道“扩散”题至少给了我们三点启示曼哈顿距离与网格BFS的等价性在四邻域网格中两点最短路径步数等于其曼哈顿距离。因此所有基于等速四向扩散/传播的问题都可以转化为曼哈顿距离的覆盖问题。这比BFS更本质也常常更高效。离散几何中的计数问题计算多个规则图形如菱形、正方形并集内的整数点个数是一类经典问题。扫描线区间合并是解决一维投影叠加的利器。如果扩散是八方向切比雪夫距离那么覆盖区域就变成了正方形问题会变得更简单。复杂度分析的直觉训练面对“无限平面”、“长时间扩散”第一反应不应该是开大数组而应该是分析增长规律。通过计算曼哈顿距离我们将一个时间O(t)、空间O(t²)的模拟问题转化为了一个时间O(t²)甚至O(t)、空间O(1)的计数问题。这种思维转换是解决竞赛难题的关键。在实际编程中我个人的习惯是先尝试最直观的暴力方法并立刻进行最坏情况下的复杂度估算。如果估算结果在可接受范围内例如本题的千万次运算就优先实现它因为它通常代码简单不易出错。如果暴力法不可行再深入分析问题结构寻找像曼哈顿距离、扫描线这样的优化突破口。这道2020年国赛题就是一个绝佳的范例展示了如何将一道看似需要模拟的题通过数学洞察变成一个简洁优美的计数问题。