能否用动态规划求解长度为n的子序列模m和≥x的计数问题?
问题解法与DP可行性分析
核心解法:折半搜索(Meet-in-the-Middle)
由于n的范围是1≤n≤42,直接枚举所有242个子序列显然不现实,但将数组拆分为两个大小相近的子集(比如21+21)后,每个子集的子序列数量仅为221=2097152,完全可以处理。具体步骤如下:
拆分原数组
将n个元素分成左右两部分,例如左半部分取前k个元素(k=min(21, n)),右半部分取剩余n-k个元素。枚举子序列和模m的结果
- 对左半部分,枚举所有可能的子序列,计算每个子序列的和对m取模的值,将结果存入数组A,然后对A排序。
- 对右半部分执行同样操作,得到排序后的数组B。
统计符合条件的组合数
总共有len(A)*len(B)个子序列组合(包含空序列)。我们需要统计其中满足(a + b) mod m ≥ x的组合数,可以通过计算补集(即(a + b) mod m < x的数量),再用总数减去补集数量得到答案。对于每个
a ∈ A,利用B的有序性,通过二分查找快速计算符合条件的b的数量:- 分两种情况分析
(a + b) mod m < x的b的范围:- 当
a + b < m时,要求b < x - a,且b ≥ 0,对应区间[0, max(-1, x - a - 1)]。 - 当
a + b ≥ m时,要求b < x + m - a,且b < m,对应区间[m - a, min(m - 1, x + m - a - 1)]。
- 当
- 对每个有效区间,用二分查找找到B中落在区间内的元素个数,累加得到当前a对应的补集数量。
最终答案 = 总组合数 - 所有a对应的补集数量之和。如果题目要求子序列非空,需额外判断空序列是否符合条件(空序列和为0,若0≥x则减1)。
- 分两种情况分析
动态规划的可行性分析
常规动态规划思路不可行:
如果定义dp[i][j]表示前i个元素中选若干个,和模m等于j的子序列数量,状态空间大小为n*m。由于m可达到10^9,这个规模无论是内存存储还是计算时间都完全无法承受。
折半后的小规模DP可选:
对于拆分后的每个子集(最多21个元素),可以用小规模DP统计子序列和模m的结果:
- 初始化
dp字典或数组,dp[0] = 1(代表空序列)。 - 对每个元素
num,更新dp:新的状态(j + num) mod m的数量加上原dp[j]的值,同时保留原状态(不选当前元素)。
不过对于21个元素,直接枚举所有子序列并计算模m结果,实现起来比DP更简单直接,效率也相近。
内容的提问来源于stack exchange,提问作者user13313695
相关产品推荐
相关产品推荐

