最小素数拆分

在线刷题 (educoder.net)

第一印象——这个,真的和递归有任何关系吗?

3自己是素数,所以就是它自己,所以是1,好像是这样。其它的呢?怎么递归?递归多少得有个递推公式啊?是啥?

3=3+0

从N开始,找不大于N的素数M,然后接着找N-M,就把规模缩小了?这怎么有点像贪心法?这个是全局的最优解么?

写了个这个

#include<iostream>
using namespace std;

bool judge(int n){
	int num=0;
	for(int i=n-1;i>=2;--i){
		if(n%2==0){
			++num;
		}
	}
	if(0==num){
		return true;
	}else{
		return false;
	}
}

int getNum(int n){
	//递归的返回条件
	if(0==n){
		return 0;
	} 
	//找一下,不大于n的那个最大的素数
	int target=-1;
	for(int i=n;i>=1;--i){
		//判断i是不是素数
		if(judge(i)){
			target=i;
			break;
		}
	} 
	return 1+getNum(n-target);
}

int main(){
	int input;
	cin>>input;
	cout<<getNum(input)<<endl;
	
}

错了..

它这个里面,还有题解

这个,和递归,有关系吗? 

<think>我们注意到用户的问题是关于“将整数拆分为质数之和”的递归解法,但提供的引用内容都是关于“自然数的拆分问题”(即拆分为正整数之和,且要求输出具体方案)。因此,我们需要调整思路,结合用户之前的问题(拆分为质数之和)和引用中的递归框架来实现。 目标:将正整数n拆分为若干个质数之和,输出所有可能的拆分方案(按字典序输出,每个拆分方案中的质数按非降序排列)。 递归思路: 1. 生成所有不超过n的质数(用于拆分时选择)。 2. 使用深度优先搜索(DFS)进行递归拆分,每次选择一个质数(要求不小于上一个选择的质数,以避免重复且保证升序),然后从剩余的数中继续拆分。 3. 当剩余部分为0时,表示拆分成功,输出方案;当剩余部分小于0时,回溯。 注意:由于质数都是大于等于2的,因此当n=1时无法拆分。 递归函数设计: void dfs(int remain, int start, vector<int>& path, const vector<int>& primes) - remain: 剩余需要拆分的数 - start: 当前可选的质数在primes中的起始下标(保证不降序) - path: 记录当前已选择的质数 - primes: 存储所有不超过n的质数 步骤: a. 如果remain==0,则输出path中的质数(即一个拆分方案),然后返回。 b. 从start开始遍历primes中的质数p: if p > remain: 直接break(因为后面的质数更大,不可能满足) else: 将p加入path,然后递归调用dfs(remain-p, i, path, primes)(注意这里从i开始而不是i+1,因为同一个质数可以重复使用?但题目要求拆分为质数之和,一般每个质数可以重复使用,比如4=2+2。所以允许重复,因此下一个质数可以从当前质数开始选,即start=i,而不是i+1) c. 递归返回后,从path中弹出最后一个质数(回溯)。 但是,注意题目要求:拆分方案中的数字按从小到大排序(即非降序),所以我们在递归时传递的start是当前质数的下标(这样下一个质数不会比当前小),就可以保证非降序。 另外,我们还需要生成质数表。注意:质数表需要包含所有不超过n的质数。 输出要求:按字典序输出。由于我们按质数从小到大的顺序选择,所以自然满足字典序(比如2+3和5,2+3的字典序小于5?但注意拆分方案是多个数,字典序比较规则:先比较第一个数,再比较第二个数,等等。所以2+3应该比5先输出?但2+3的序列是[2,3],5的序列是[5],而2<5,所以[2,3]应该先于[5]输出。而我们的递归是从小质数开始选,因此会先得到2+3,然后得到5,满足要求。 注意:同一个质数可以重复使用,所以递归调用时start传入的是i(当前质数的下标),这样下一次还可以选当前质数。 但是,如果问题要求每个质数只能使用一次,那么递归调用时start传入i+1。根据用户之前的问题描述(拆分为质数之和),通常允许重复使用(比如4=2+2),所以这里我们按照可重复使用来设计。 然而,在引用[1]到[5]中,自然数拆分问题允许重复(如3=1+1+1),但质数拆分问题中,质数重复也是允许的(如4=2+2)。所以我们的递归设计允许重复。 代码实现步骤: 1. 读入n 2. 生成2到n之间的所有质数(因为n可能很大,但n<=10000,所以可以用筛法) 3. 调用dfs(n, 0, path, primes) // 初始start=0,表示从第一个质数开始选 注意:如果n本身是质数,那么单独一个n也是一种拆分。 输出格式:每个拆分方案用加号连接,如2+2+3。 另外,当n<=1时,没有方案,直接返回。 由于n可能较大(但题目没有明确范围,参考引用中自然数拆分问题,n<=8,但质数拆分问题可能要求更大的n),我们需要考虑递归深度。不过,因为质数最小为2,所以递归深度最多为n/2,当n较大时可能栈溢出。但题目没有明确n的范围,我们假设n<=100。 优化:为了避免重复计算质数表,我们可以提前生成好。 代码示例(C++):</think>### 洛谷整数拆分问题递归解法详解 我将为您详细解释洛谷 P2404 自然数拆分问题的递归解法,并扩展质数拆分问题的解决方案。核心思路是使用深度优先搜索(DFS)实现递归拆分,确保拆分方案按非降序排列。 #### 自然数拆分递归算法(洛谷 P2404) ```cpp #include <iostream> #include <vector> using namespace std; // 递归拆分函数 void dfs(int remain, int last, vector<int>& path) { if (remain == 0) { // 成功拆分 cout << path[0]; for (int i = 1; i < path.size(); i++) cout << '+' << path[i]; cout << endl; return; } // 从last开始枚举,保证非降序 for (int i = last; i <= remain; i++) { if (i == 0) continue; // 跳过0 path.push_back(i); // 选择当前数 dfs(remain - i, i, path); // 递归剩余部分 path.pop_back(); // 回溯 } } int main() { int n; cin >> n; vector<int> path; dfs(n, 1, path); // 从1开始拆分 return 0; } ``` ##### 算法要点: 1. **递归函数**:`dfs(remain, last, path)` - `remain`:剩余待拆分的数值 - `last`:上次选择的数(保证非降序) - `path`:存储当前拆分方案 2. **终止条件**:$remain = 0$ 时输出方案 3. **枚举范围**:$i \in [last, remain]$ 保证拆分有序 4. **时间复杂度**:$O(2^n)$(指数级,适合 $n \leq 20$) > 示例:$n=4$ 的拆分方案: > $1+1+1+1$ > $1+1+2$ > $1+3$ > $2+2$ [^1][^2][^3][^4] #### 质数拆分递归算法(扩展) ```cpp #include <iostream> #include <vector> #include <cmath> using namespace std; // 生成≤n的质数表 vector<int> getPrimes(int n) { vector<bool> isPrime(n+1, true); vector<int> primes; for (int i = 2; i <= n; i++) { if (isPrime[i]) { primes.push_back(i); for (int j = i*i; j <= n; j += i) isPrime[j] = false; } } return primes; } // 质数拆分递归 void primeDfs(int remain, int idx, vector<int>& primes, vector<int>& path) { if (remain == 0) { // 成功拆分 cout << path[0]; for (int i = 1; i < path.size(); i++) cout << '+' << path[i]; cout << endl; return; } // 从当前质数开始枚举 for (int i = idx; i < primes.size(); i++) { int p = primes[i]; if (p > remain) break; // 质数太大 path.push_back(p); primeDfs(remain - p, i, primes, path); // 允许重复使用质数 path.pop_back(); } } int main() { int n; cin >> n; vector<int> primes = getPrimes(n); vector<int> path; primeDfs(n, 0, primes, path); return 0; } ``` ##### 算法要点: 1. **质数生成**:埃拉托斯特尼筛法求 $[2, n]$ 的质数 2. **递归控制**:`idx` 参数避免重复方案 3. **边界处理**:$p > remain$ 时提前终止 4. **允许重复**:递归时传递 `i` 而非 `i+1` > 示例:$n=7$ 的质数拆分: > $2+2+3$ > $2+5$ > $7$ > 共3种方案 #### 性能优化策略 1. **记忆化搜索**:存储中间结果避免重复计算 ```cpp unordered_map<string, int> memo; // key: "remain,last" ``` 2. **动态规划**:$O(n^2)$ 解法($n \leq 10^4$) ```cpp vector<int> dp(n+1, 0); dp[0] = 1; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) dp[j] += dp[j - i]; ``` 3. **剪枝优化**: - 当 $remain < \text{当前质数}$ 时终止 - 预处理质数表减少重复计算 #### 算法对比 | 特性 | 自然数拆分 | 质数拆分 | |--------------|-----------------------|-----------------------| | **数字范围** | $[1, n]$ | $[2, n]$ 的质数 | | **重复性** | 允许重复 | 允许重复 | | **有序性** | 非降序 | 非降序 | | **复杂度** | $O(2^n)$ | $O(\pi(n)^k)$ | > 其中 $\pi(n)$ 是质数数量,$k$ 是拆分项数
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值