C++深度优先搜索经典例题之自然数的拆分问题

题目描述

任何一个大于 1 的自然数 n,总可以拆分成若干个小于 n 的自然数之和。现在给你一个自然数 n,要求你求出 n 的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。

输入格式

输入:待拆分的自然数 n。

输出格式

输出:若干数的加法式子。

输入

7

输出

1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

思路创作

首先,我们需要理解题目要求:给定一个自然数 n,我们需要找出它的所有拆分方式,并且按照字典序输出。一个数的拆分是指将其表示为若干个正整数之和,不考虑顺序。

接下来,我会考虑如何生成这些拆分。由于拆分是无序的,我们可以考虑使用深度优先搜索(DFS)的方法来遍历所有可能的拆分。DFS 的特点是能够系统地探索所有可能的路径。

在 DFS 的过程中,我们需要维护几个关键的信息:

  1. 当前已经拆分的和(我们称之为 remain),它初始化为 n,表示还需要拆分的剩余部分。
  2. 当前拆分的路径(我们称之为 path),它是一个数组或者类似的数据结构,用于存储当前正在构建的拆分中的各个数字。
  3. 搜索的起始点(我们称之为 start),它表示在当前路径的下一个位置可以放置的最小数字。这通常设置为当前路径中最后一个数字加 1,以确保拆分的字典序。

然后,我们可以编写一个递归函数来实现 DFS。在递归的每一步中,我们尝试从 start 开始的所有可能的数字,将它们添加到 path 中,并递归地调用自身来处理剩余的 remain。当 remain 减到 0 时,说明我们找到了一个有效的拆分,可以输出 path

为了避免栈溢出,我们需要确保递归的深度是有限的。这可以通过限制 path 的长度来实现,或者使用其他不需要递归的数据结构(如队列)。但是,在这个简单的实现中,我们假设 n 不会太大,以至于递归深度不会导致问题。

最后,在 main 函数中,我们读取输入的 n,初始化 path 和其他必要的变量,然后调用 DFS 函数开始搜索。

通过这种方式,我们可以系统地生成并输出给定自然数 n 的所有拆分,确保它们是按照字典序排列的。

DFS 函数

void dfs(int n, int path[], int depth, int start) { 
// ... 函数体 ... 
}

 

作用

  • 实现深度优先搜索算法,用于生成并输出自然数 n 的所有拆分。
  • n 是当前还需要拆分的剩余部分。
  • path 是当前拆分的路径,存储已经选择的数字。
  • depth 是当前路径的深度或长度。
  • start 是下一个数字的最小可能值,用于确保拆分的字典序。

DFS 函数体内部

终止条件+输出
if (n == 0 && depth > 0) {  
    for (int i = 0; i < depth - 1; ++i) {  
        cout << path[i] << "+";  
    } 
    if(path[depth-1]!=m){
       cout<<path[depth-1]<<endl;
    } 
    return;  
} 

作用

  • 当 n 减到 0 且路径长度大于 0 时,输出当前路径。
  • path[depth-1]!=m为了不输出最后一个数字n所以要判断。
递归调用
for (int i = start; i <= n; ++i) { 
    path[depth] = i; 
    dfs(n - i, path, depth + 1, i); 
}

作用

  • 遍历从 start 到 n 的所有可能数字 i
  • 将 i 添加到当前路径 path 中。
  • 递归调用 dfs 函数,处理剩余部分 n - i,并增加路径深度。

主函数

int main() { 
    int n; 
    cin >> n; // 输入待拆分的自然数 n 
    dfs(n, path, 0, 1); // 从 1 开始搜索可能的拆分 
    return 0; 
}

作用

  • main 函数是程序的入口点。
  • 读取用户输入的自然数 n
  • 初始化路径数组 path
  • 调用 dfs 函数开始搜索并输出所有拆分。

通过这些代码块,我们可以系统地生成并输出给定自然数 n 的所有拆分,确保它们是按照字典序排列的,并且避免了栈溢出的问题(通过限制路径的最大长度)。

整体代码实现

#include <iostream>  
using namespace std;  
int n,m;
void dfs(int n, int path[], int depth, int start) {  
    // 如果当前的和等于 n,且路径长度大于 0,则输出当前路径  
    if (n == 0 && depth > 0) {  
        for (int i = 0; i < depth - 1; ++i) {  
            cout << path[i] << "+";  
        } 
        if(path[depth-1]!=m){
        	cout<<path[depth-1]<<endl;
		} 
        return;  
    }  
    // 从 start 开始遍历可能的数字  
    for (int i = start; i <=n; ++i) {  
        // 将当前数字添加到路径中  
        path[depth]= i;  
        // 递归调用 dfs,处理剩余的部分  
        dfs(n - i, path, depth + 1, i);  
    }  
}  
  
int main() {    
    cin >> n; // 输入待拆分的自然数 n  
    m=n;
    int path[20]; // 用于存储当前拆分的路径  
    dfs(n, path, 0, 1); // 从 1 开始搜索可能的拆分  
    return 0;  
}

运行结果

如有不足欢迎指出

  • 24
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值