洛谷P2404 自然数的拆分问题

洛谷P2404 自然数的拆分问题

题目描述
任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。现在给你一个自然数n,要求你求出n的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。
首先分析题目,给任意一个数,比如7,要把它拆成小于7的各个数相加,例如1+1+1+1+1+1+1,而且要输出所有序列,这就需要用到搜索,由于拆分后的序列中的数字要按从小到大输出,所以此题应该用深搜。
一开始我想这题的时候,对于深搜如何剪枝,一直不好怎么写。在看了一些大佬的思路后,学习到了一种思路。
下面放代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#define MAXN 0x3f3f3f3f
using namespace std;
int a[1000]={1};
void dfs(int m,int t)//兄弟们 开搜了 奥里给!m就是输入的自然数大小,t表示可以拆分自然数的个数
{
    if (m == 0)
    {
        if(t!=2)//如果没有这个,最后会单独输出自然数n
        {cout << a[1];}
        
        for (int i = 2; i <t;i++)//输出拆分的序列
        {
            cout <<'+'<<a[i] ;
        }
        cout << endl;
        return;
    }

    else
    {
        for (int i = a[t - 1]; i <= m;i++)//i=a[t-1]这个操作就很巧妙,首先,题目中说这些序列要从小到大,而a[]这个数组,我们一开始将a初始化为1,后面搜每次都是从a[t]开始,比如第一次搜的是a[1]=2;那么第二次搜就是从a[2]=2开始,也就是说越往后面的序列数,只会大于等于前面的数,而不会小于等于前面的数。
        {
            a[t] = i;
            m -= i;
            dfs(m, t + 1);
            m += i;//回溯 比如搜完了第一个数为1的,从第一个数为2开始 就必须回溯到最开始m的状态。
        }
    }
    
}
int n;
int main()
{
    int n;
    cin >> n;
    dfs(n, 1);
    return 0;
}

还有一种不用回溯的

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#define MAXN 0x3f3f3f3f
using namespace std;
int n, m;
int counts =0;
int a[100000]={1};
void dfs(int m,int t )
{
    if(m==0)
    {
        if(t!=2)
            cout << a[1];
        for (int i = 2; i <= t-1;i++)
        {
            cout << '+'<<a[i];
        }
        cout << endl;
    }
    else{
        for (int i = a[t-1]; i <= m;i++)
        {
            a[t] = i;
            dfs(m-a[t], t + 1);//不用回溯
        }
    }
}
int main()
{
    cin >> n;
    dfs(n, 1);
    return 0;
}
原文地址:https://www.cnblogs.com/a821403286/p/13621575.html