Skip to content
On this page

凹语言中文版NOIP 2024真题详解第1题


NOIP 是全国青少年信息学奥林匹克联赛的英文缩写,是面向中小学生的信息学竞赛。由中国计算机学会主办,是国内信息学领域权威的普及性竞赛。主要考察编程能力和算法思维,核心语言包括 C++(主流)、C、Pascal(部分组别)。参赛对象为小学、初中、高中学生,分提高组、普及组等组别。成绩优秀者可获得高校自主招生、综合评价等相关优惠,是信息学特长升学的重要参考。

作为中国乃至面向世界的赛事,NOIP 比赛使用的全部是国外的编程语言。本文尝试通过凹语言来讲解答,通过不同的视角体会算法问题和工程问题的差异。

1. NOIP 2024 第1题

如果暂时不考虑性能(即 $O(n^2)$ 甚至更慢的复杂度也没关系),我们能找到一个更直观、更容易理解的算法,这个算法被称为最大流/最小费用最大流模型,或者用更简单的语言描述:最大二分图匹配的变形。

但是,因为这个题目的特殊性质(只有 '0' 和 '1' 两种字符,且操作是相邻交换),我们可以避开复杂的图论模型,直接使用纯粹的贪心和计数,这是最容易理解且最高效的方法。

2. 💡 最易理解的算法:纯粹的贪心与计数

这个算法的思路是:将问题分解为独立的两部分,并分别用贪心法解决。

2.1. 核心洞察:字符的分类与自由度

所有的字符和位置都可以根据它们能否被交换,被分成四种类型:

$s_1$ 的状态 ($t_{1,i}$)$s_2$ 的状态 ($t_{2,i}$)位置 $i$ 的自由度决策:匹配 $s_{1,i}$ 和 $s_{2,i}$
0 (固定)0 (固定)0固定匹配/不匹配。 $s_{1,i}$ 必须对 $s_{2,i}$。
0 (固定)1 (可换)1$s_{1,i}$ 确定。用 $s_2$ 的可换字符池去匹配它。
1 (可换)0 (固定)1$s_{2,i}$ 确定。用 $s_1$ 的可换字符池去匹配它。
1 (可换)1 (可换)2完全自由。 用 $s_1, s_2$ 剩余的字符池进行自由组合。

2.2. 算法核心:两个独立的池子

因为相邻交换的特性(只要没有 $t_{i}=0$ 的障碍物,可换字符可以在其区间内任意排列),我们可以把所有 $t_{1,i}=1$ 的字符想象成一个大池子 $C_1$,所有 $t_{2,i}=1$ 的字符想象成一个大池子 $C_2$

目标: 最大化从 $C_1$ 和 $C_2$ 中取出相同字符进行匹配的次数。

2.3. 三步贪心策略

我们将匹配过程分为三步,每一步都采用贪心策略,因为一旦完成了匹配,这个字符就不能再被使用了(即:先用先得)。

步骤 1:处理双固定的位置(类型 0)

  • 操作: 遍历所有 $i$,如果 $t_{1,i}=0$ 且 $t_{2,i}=0$。
  • 贪心: 如果 $s_{1,i} = s_{2,i}$,这是一个确定无疑的匹配
  • 效果: 答案 $+1$。

步骤 2:处理一固一换的位置(类型 1)

  • 操作: 遍历所有 $i$ 属于类型 1 的位置($t_{1,i} \neq t_{2,i}$)。
  • 贪心: 在这类位置上,我们必须用 $s_1$ 或 $s_2$ 中固定的字符去锁定匹配目标,然后消耗掉另一个字符串的字符池中的一个对应字符。
    • 例: $t_{1,i}=0, t_{2,i}=1$ 且 $s_{1,i} = '0'$。如果 $C_2$ 池子中还有 '0',我们必须用 $C_2$ 中的一个 '0' 来匹配这个固定的 $s_{1,i}$,从而锁定这个匹配。
  • 效果:
    • 如果匹配成功,答案 $+1$。
    • 相应地,从 $C_1$ 或 $C_2$ 中减少对应字符的计数。

为什么先处理这个? 因为这些固定字符是必须要匹配的(如果可能),它们消耗了 $C_1$ 或 $C_2$ 的资源,限制了接下来的自由组合。

步骤 3:处理双可换的位置(类型 2)

  • 操作: 此时 $C_1$ 和 $C_2$ 池子中还剩下一些字符,以及一些 $t_{1,i}=1, t_{2,i}=1$ 的空闲位置
  • 贪心: 由于这些空闲位置是完全自由的,我们可以用 $C_1$ 中剩余的字符和 $C_2$ 中剩余的字符进行最大化匹配
    • 匹配 '0': 我们可以组成 $\min(\text{剩余 '0' in } C_1, \text{剩余 '0' in } C_2)$ 对。
    • 匹配 '1': 我们可以组成 $\min(\text{剩余 '1' in } C_1, \text{剩余 '1' in } C_2)$ 对。
  • 效果: 答案加上这些匹配的总和。

3. 开始解决正题

这个算法把一个复杂的排列问题,简化成了资源分配问题:

  1. 确定不需要资源的匹配。
  2. 贪心分配资源($C_1$ 和 $C_2$)给那些半固定的需求。
  3. 最大化利用剩余的资源进行自由组合。

由于我们总是在可能的情况下优先匹配(贪心),且我们没有丢失任何字符($C_1$ 和 $C_2$ 中包含了所有可换字符),这个策略是正确的。

凹语言中文编程的代码片段如下:

完整的代码:https://gitcode.com/wa-lang/wa/blob/master/waroot/examples/noip2024/edit.wz

4. 结语:在实战中打磨中文编程

之前我们已经通过凹语言中文编程展示了CSP-J题目的解法,今天我们更进一步给出了NOIP题目的解法。正如我国回归联合国的过程充满了曲折,中文编程想走向世界的道路也注定不会平坦。最终的结果是汉语成了联合国的官方工作语言之一,中文编程也必将在世界上占有一席之地。