百年校庆临近,你被拉进了筹备组,负责「数据与排班」这一块。组长一口气扔来三份需求,每一份看起来都不难,可仔细一想都不好惹。
难题一(纪律委员的座位表):校庆当天要在 n × n 的观礼方阵里安排 K 名纪律委员。为了互相照应,任意两名委员不能坐在相邻的格子里——注意是「八邻接」,也就是同一行相邻、同一列相邻、以及斜对角相邻都算相邻。请问一共有多少种安排方案?n ≤ 9,K ≤ n²。
难题二(幸运编号的筛选):校庆要发一批纪念徽章,编号从 A 到 B。组委会想把「相邻两位数字之差至少为 2」的编号挑出来做纪念款(比如 130 就不行,因为 |3−0| = 3 其实可以,但 12 不行,因为 |2−1| = 1)。A 和 B 可能高达 2×10⁹,请问一共有多少个纪念款编号?
难题三(实时监控窗口):大屏上要滚动展示各学院的捐款数据,屏幕一次只能显示连续 k 个学院的数据。为了让大屏更有冲击力,每滑动一格,都要立即报出当前窗口内的最大捐款额。数据点有 10⁶ 个,每一次都重新扫一遍窗口太慢了。
三道难题,正好对应本讲的两大主题。
• 难题一的难点在于「当前这一行怎么排」这件事的信息量太大——但如果把「某一行的排布」编码成一个二进制整数(1 表示有人、0 表示空着),状态立刻就能装进数组下标了。这就是状态压缩动态规划;
• 难题二的难点在于「数字太大,没法一个一个枚举」——但如果一位一位地填,每一位只关心「上一位填了什么」,状态就只有十几个,这就是数位 DP;
• 难题三的难点在于「窗口内有大量无用信息」——每次滑动时,那些「又小又靠左」的元素永远不可能成为答案,可以直接丢掉。用一个单调队列维护候选集合,就把 O(nk) 压缩到了 O(n)。这就是单调队列优化。
把集合压成整数、把大数字按位拆开、把无用的候选提前淘汰——这是提高组 DP 的三大「降维」利器。
💡 故事背后的算法思想
共同点:三道题都在面对同一个敌人——状态空间太大。状压的基本思路是「把集合编码成整数,让状态空间从「组合数」变成「2ⁿ」」;数位 DP 的基本思路是「按位拆分,把 10⁹ 规模的枚举变成 10 位 × 10 种选择的 DP」;单调队列优化的基本思路是「把一个 O(k) 的扫描,用数据结构均摊成 O(1)」。
它们的关系:状压与数位 DP 属于「重新设计状态,让状态数变少」;单调队列优化属于「状态不变,但把转移的代价降低」。这两条路线合起来,就是竞赛中说的「DP 优化」。
工程意义:提高组的很多题,朴素 DP 想得到,但过不了——卡的就是「状态数」和「转移代价」这两样。本讲的核心训练目标就是:看到「n ≤ 20」想到状压,看到「求 [L,R] 满足条件个数」想到数位 DP,看到「f[i] 由某个区间的最值转移」想到单调队列。