📅  最后修改于: 2023-12-03 15:11:34.687000             🧑  作者: Mango
素子集积问题是指在一个集合中选择若干个数相乘,使得其积为素数的方案数。
朴素算法就是枚举所有可能的子集,计算每个子集对应的积是否为素数。时间复杂度为 $2^n$。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
bool is_prime(int n) {
if (n <= 1)
return false;
for (int i = 2; i <= sqrt(n); ++i) {
if (n % i == 0)
return false;
}
return true;
}
int subset_product(vector<int>& nums) {
int res = 0;
int n = nums.size();
for (int i = 1; i < (1 << n); ++i) {
int p = 1;
for (int j = 0; j < n; ++j) {
if (i & (1 << j)) {
p *= nums[j];
}
}
if (is_prime(p)) {
++res;
}
}
return res;
}
int main() {
vector<int> nums{2, 3, 5, 7};
int res = subset_product(nums);
cout << res << endl; // 8
return 0;
}
我们首先可以枚举出所有小于 $n$ 的质数 $p_1,p_2,p_3,\cdots,p_m$,然后对于每个 $p_i$,枚举出所有可能的子集,计算其积是否为 $p_i$。这样我们就能够得出所有积为素数的方案数。
但是,求出小于 $n$ 的质数本身就是一个比较费事的问题。所以我们可以先用埃氏筛法求出所有小于 $n$ 的素数,然后再进行枚举。时间复杂度为 $O(2^mp(m))$,其中 $p(m)$ 表示小于等于 $m$ 的素数个数。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int get_primes(int n, vector<int>& primes) {
vector<bool> is_prime(n + 1, true);
int cnt = 0;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
primes.push_back(i);
++cnt;
}
for (auto p : primes) {
if (i * p > n)
break;
is_prime[i * p] = false;
if (i % p == 0)
break;
}
}
return cnt;
}
int subset_product(vector<int>& nums) {
vector<int> primes;
int cnt = get_primes(*max_element(nums.begin(), nums.end()), primes);
int res = 0;
int n = nums.size();
for (int i = 0; i < cnt; ++i) {
int p = primes[i];
for (int j = 1; j < (1 << n); ++j) {
int t = 1;
for (int k = 0; k < n; ++k) {
if (j & (1 << k)) {
t *= nums[k];
}
}
if (t == p) {
++res;
}
if (t > p) {
break;
}
}
}
return res;
}
int main() {
vector<int> nums{2, 3, 5, 7};
int res = subset_product(nums);
cout << res << endl; // 8
return 0;
}
容斥原理是一种比较常用的计数方法,对于一些比较难计算的问题可以起到简化的作用。对于素子集积问题,我们可以考虑容斥原理:对于每个素数 $p$,将所有积为 $p$ 的子集个数相加,减去所有积为 $p_1p_2$ 的子集个数,再加上所有积为 $p_1p_2p_3$ 的子集个数……以此类推。这样就可以得到积为素数的子集个数。
时间复杂度为 $O(3^np(n))$,其中 $p(n)$ 表示小于等于 $n$ 的素数个数。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int subset_product(vector<int>& nums) {
int n = nums.size();
int res = 0;
for (int i = 1; i < (1 << n); ++i) {
int p = 1, cnt = 0;
for (int j = 0; j < n; ++j) {
if (i & (1 << j)) {
p *= nums[j];
++cnt;
}
}
if (cnt % 2 == 1) {
res += 1;
} else {
res -= 1;
}
}
return res;
}
int main() {
vector<int> nums{2, 3, 5, 7};
int res = subset_product(nums);
cout << res << endl; // 8
return 0;
}
对于素子集积问题,我们有多种解法,包括朴素算法、埃氏筛法和容斥原理。其中,实际应用中,基本不会使用朴素算法。埃氏筛法和容斥原理的时间复杂度都比较高,但对于规模不太大的数据都是可行的。需要根据具体情况灵活选择算法。