[信息与未来 2016] 素数分解
筛出≤n的所有素数,再做0/1背包计数取max:dp[j]=max(dp[j], dp[j-p]+1),求最多项数。
OJ: luogu
题目 ID: B4141
难度:普及-
标签:动态规划01背包素数
日期: 2026-08-08 23:13
题意
输入一个正整数
例如
思路
一句话本质:在不超过
选出哪些素数才能让项数最多?
题面要求把
"互不相同"给定了什么约束?
它强制每个素数在整个分解中最多出现一次。所以不能把一个素数重复使用,只能是每个素数用 0 次或 1 次。
选或不选时,状态需要记住什么?
对于每一个素数
因此设
转移顺序怎么定?
外层枚举素数列表中的每一个
其中
转移为什么在
因为"选
最终答案是
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 205;
int n;
bool is_prime[MAXN]; // 埃氏筛标记
vector<int> primes; // ≤n 的所有素数
// dp[j] 表示和为 j 的素数分解方案数(这里求的是最多项数)。
int dp[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
fill(is_prime, is_prime + n + 1, true);
is_prime[0] = is_prime[1] = false;
for (int i = 2; i * i <= n; i++) {
if (is_prime[i]) {
for (int j = i * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
for (int i = 2; i <= n; i++) {
if (is_prime[i]) primes.push_back(i);
}
// dp 初始化为 -1 表示不可达,dp[0] = 0 表示和为 0 用 0 项。
fill(dp, dp + n + 1, -1);
dp[0] = 0;
// 0/1 背包:每个素数最多用一次,从大到小编排容量。
for (int p : primes) {
for (int j = n; j >= p; j--) {
if (dp[j - p] != -1) {
dp[j] = max(dp[j], dp[j - p] + 1);
}
}
}
cout << dp[n] << '\n';
return 0;
}复杂度
- 筛素数:
- DP:
,其中 是不超过 的素数个数, 时数值很小 - 空间复杂度:
- 总体在
下轻松跑过
总结
这题把"素数分解"和"背包计数取 max"结合在一起。筛出素数之后就和普通的 0/1 背包一样:每件物品重量为