整数划分相关

整数划分的核心思想

1. 问题本质

把整数 \(m\) 分成 \(n\) 个数,顺序无关。比如把 5 分成 2 个数:

  • \(1+4\)、\(2+3\) 算两种不同方案
  • 但 \(1+4\) 和 \(4+1\) 算同一种方案

2. 如何避免重复计数?

既然顺序无关,我们就强制规定一个顺序,比如非递减:

\[a_1 \le a_2 \le \dots \le a_n \]

这样 \(1+4\) 合法,\(4+1\) 非法,从根本上杜绝了重复。

3. 两个“基本操作”

假设我们已经有了一个合法的非递减序列,怎么“生成”更多的序列?反过来,怎么“拆解”一个序列?

操作 A:在序列最前面加一个 1

[3, 5]  ->  [1, 3, 5]

这对应“原序列的第一项是 1”的情况。

操作 B:给所有数都加一个常数 k

[3, 5]  ->  [5, 7]   (k=2)

这对应“原序列的第一项 > 1”的情况(如果第一项是 1,没法全体加 k 还保持非递减的“第一项是 1”这个特征,因为 \(1+k \ne 1\))。

关键点:这两个操作是互逆的,而且能覆盖所有情况。任何非递减序列,要么第一项是 1(用操作 A 拆),要么第一项 > 1(用操作 B 拆)。


这里补充一下什么是“可重复”,“可重复”说的是划分结果中,同一个数字可以出现多次,也可以理解为“每个数字的使用次数不限”。

我们看下面的例子:

划分目标 可重复 不可重复
\(6\) 分成 \(3\) 个数 \(1+1+4\),\(1+2+3\),\(2+2+2\)(\(3\) 种) \(1+2+3\)(\(1\) 种)
\(8\) 分成 \(2\) 个数 \(1+7\),\(2+6\),\(3+5\),\(4+4\)(\(4\) 种) \(1+7\),\(2+6\),\(3+5\)(\(3\) 种,\(4+4\) 不合法)

六种情况

情况 1.1:可重复,无上限

问题:把 \(m\) 分成 \(n\) 个正整数,可重复。

状态:\(f[i][j]\) = 把 \(j\) 分成 \(i\) 个正整数的方案数(非递减)。

推导:

对一个合法序列 \(a_1 \le a_2 \le \dots \le a_i\),和为 \(j\):

我们有两种操作方式:

  1. 在一个合法序列前面插入一个 \(1\),序列长度变为 \(i+1\),总和变成 \(j+1\),所以 \(f[i+1][j+1]+=f[i][j]\),也就是 \(f[i][j]+=f[i-1][j-1]\)。
  2. 将一个合法序列里的每一项加上 \(1\),序列长度还是 \(i\),总和变成 \(j+i\),所以 \(f[i][j+i]+=f[i][j]\),也就是 \(f[i][j]+=f[i][j-i]\)。

递推式:

\[f[i][j] = f[i-1][j-1] + f[i][j-i] \]

边界:\(f[0][0] = 1\)。


情况 1.2:不可重复,无上限

问题:把 \(j\) 分成 \(i\) 个互不相同的正整数。

状态:\(f[i][j]\) 依然是非递减序列(但由于不可重复,实际上是严格递增 \(a_1 < a_2 < \dots < a_i\))。

  1. 整体加 1:
    \(f[i][j+i] += f[i][j]\) (也就是 \(f[i][j] += f[i][j-i]\))。

  2. 整体加 1,再在开头添一个 1:
    不难发现,这是依然可以保持严格递增的。

    \(f[i+1][j+i+1] += f[i][j]\),反推得 \(f[i][j] += f[i-1][j-i]\)。

递推式:

\[f[i][j] = f[i-1][j-i] + f[i][j-i] \]


情况 2.1:可重复,全奇数

问题:把 \(j\) 分成 \(i\) 个奇数,可重复。
状态:\(f[i][j]\) 是非递减的奇数序列(如 \([1, 1, 3, 5]\))。

推导:

如果手里是奇数,怎么保持奇数?

  1. 在前面加一个 1:
    所以:\(f[i+1][j+1] += f[i][j]\),也就是 \(f[i][j] += f[i-1][j-1]\)。

  2. 整体加 2:
    所以:\(f[i][j+2i] += f[i][j]\),也就是 \(f[i][j] += f[i][j-2i]\)。

递推式:

\[f[i][j] = f[i-1][j-1] + f[i][j-2i] \]


情况 2.2:不可重复,全奇数 (重点看这里,印证你的理解)

问题:把 \(j\) 分成 \(i\) 个互不相同的奇数。
状态:\(f[i][j]\) 是严格递增的奇数序列(如 \([1, 3, 7]\))。

推导:

手里是严格递增的奇数序列,怎么长大?

  1. 整体加 2:
    所以:\(f[i][j+2i] += f[i][j]\),也就是 \(f[i][j] += f[i][j-2i]\)。

  2. 整体加 2,再在开头添一个 1:
    所以:\(f[i+1][j+2i+1] += f[i][j]\),也就是 \(f[i][j] += f[i-1][j-2i+1]\)。

递推式:

\[f[i][j] = f[i-1][j-2i+1] + f[i][j-2i] \]


3.1:可重复,每个数 < \(x\)

推导:

从 1.1 的“加操作”出发

1.1 的递推式:

\[f[i][j] = f[i-1][j-1] + f[i][j-i] \]

对应的两种“生成”操作:

操作 效果 对状态的影响
前面加 1 序列长度 +1,总和 +1 \(f[i-1][j-1] \rightarrow f[i][j]\)
全体加 1 长度不变,总和 +i \(f[i][j-i] \rightarrow f[i][j]\)

现在加上限制:每个数都 \(< x\)。

我们需要分别检查这两种操作会不会产生非法序列。


  • 第一种操作:前面加 1

从合法序列 \(f[i-1][j-1]\) 出发,在最前面插入一个 1:

\[[1, a_1, a_2, \dots, a_{i-1}] \]

会不会产生非法?

  • 新加入的数是 1,而 \(1 < x\) 恒成立
  • 剩下的 \(i-1\) 个数本来就是合法的(都 \(< x\))

所以这条路径永远安全,不需要任何减法。

第一项仍然是:

\[f[i-1][j-1] \]


  • 第二种操作:全体加 1

从合法序列 \(f[i][j-i]\) 出发,对所有数都加 1:

\[[a_1+1, a_2+1, \dots, a_i+1] \]

什么时候会出问题?

  • 合法状态下,每个数都 \(< x\),即最大只能是 \(x-1\)
  • 全体加 1 之后,如果某个数原本是 \(x-1\),加完就变成了 \(x\)
  • 而 \(x\) 是不允许的(要求 \(< x\))

所以问题出在:\(f[i][j-i]\) 里包含了 \(x-1\) 的方案。


关键:从 \(f[i][j-i]\) 中剔除含 \(x-1\) 的方案

假设 \(f[i][j-i]\) 中有一个合法序列:

\[a_1 \le a_2 \le \dots \le a_i \]

其中恰好某个位置上的数是 \(x-1\)。

我们把这个 \(x-1\) 从序列中删掉:

  • 剩下的数有 \(i-1\) 个
  • 因为原序列是非递减的,删掉一个数后仍然非递减
  • 所有剩下的数仍然都 \(< x\)(因为它们本来就都 \(\le x-1\),删掉的只是其中一个 \(x-1\),剩下的依然 \(\le x-1\))

剩下的序列的和是多少?

原来的和是 \(j-i\),删掉的数是 \(x-1\),所以:

\[(j-i) - (x-1) = j - i - x + 1 = j - x - i + 1 \]

因此,含 \(x-1\) 的方案数和以下状态一一对应:

\[f[i-1][j-x-i+1] \]


正确的递推式

把三种情况拼起来:

\[f[i][j] = \underbrace{f[i-1][j-1]}_{\text{前面加 1}} + \underbrace{f[i][j-i]}_{\text{全体加 1}} - \underbrace{f[i-1][j-x-i+1]}_{\text{扣掉全体加 1 产生的非法方案}} \]


3.2:不可重复,每个数 < \(x\)

推导:

从 1.2(不可重复,无上限)的递推式出发:

\[f[i][j] = f[i-1][j-i] + f[i][j-i] \]

这里两个分支都可能产生非法序列。非法序列长什么样?

  • 因为所有数都要 \(< x\),非法序列只有一个可能:其中恰好有一个数等于 \(x\)。
  • 为什么不可能有更多问题?因为基础递推只能生成从合法状态通过“全体 +1”得到的序列,而合法状态中最大值为 \(x-1\),加 1 后最大值为 \(x\),不会超过 \(x\)。
  • 又因为“不可重复”,序列中不可能出现两个 \(x\)(出现了就重复了)。

所以,非法序列的结构必然是:

恰好有一个数等于 \(x\),其余 \(i-1\) 个数都 \(< x\) 且互不相同。

关键一步:去掉那个非法的 \(x\)

把非法序列中的那个 \(x\) 删掉,剩下的 \(i-1\) 个数:

  • 互不相同
  • 每个数都 \(< x\)
  • 总和为 \(j - x\)

这恰好就是一个合法的 \(f[i-1][j-x]\) 的划分!

反过来,任意一个 \(f[i-1][j-x]\) 的合法划分,在末尾(或正确的位置)添上一个 \(x\),就得到一个非法序列。

因此:

非法方案数 = \(f[i-1][j-x]\)

所以递推式减去它即可:

\[f[i][j] = f[i-1][j-i] + f[i][j-i] - f[i-1][j-x] \]

posted @ 2026-08-25 20:36  MistyPost  阅读(11)  评论(0)    收藏  举报