数的计算
题目描述
我们要求找出具有下列性质数的个数(包含输入的自然数nnn):
先输入一个自然数n(n≤1000),然后对此自然数按照如下方法进行处理:
1.不作任何处理;
2.在它的左边加上一个自然数,但该自然数不能超过原数的一半;
3.加上数后,继续按此规则进行处理,直到不能再加自然数为止.
输入格式
1个自然数n(n≤1000)
输出格式
1个整数,表示具有该性质数的个数。
输入样例
6
输出样例
6
说明/提示
满足条件的数为
6,16,26,126,36,136
题目分析
方法一 (递归)
对于后一个数总是小于等于前一个数的一半,直到不能再次分割为止,如:6的后一个如果为1那么就不能再次分割。
由分析知道如若要得出满足条件数的总数,我们需要进行遍历满足条件的所有数,因此可以使用递归来书写。
#include<iostream>
using namespace std;
int num=0;
void dfs(int n)
{
num++;
for(int i=n/2;i>=1;i--)dfs(i);
}
int main()
{
int x;
cin>>x;
dfs(x);
cout<<num<<endl;
return 0;
}
方法二(递归加记忆搜索)
由快速排序的分支可知,快拍的时间复杂度为O(nlogn),上述的递归的分支大于快速排序,因此,时间复杂度坑定大于O(nlogn),因此当处于极限条件下时,纯递归方法会超时。因此可以使用记忆搜索的方式对以及得出结论的部分直接返回,以减少分支。
如 由 8我们知道可以存在8 2这种数字,但是2还可以向下分支。
得 2 与2 1两种,这是可以记录2的后续种数。
当遇到如8 4时需要对4进行分支,4的分支中存在2的情况,这时可以直接加上2的结果以达到优化。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1010;
int b[N],x;
int dfs(int n)
{
if(n==1 || b[n]!=1)return b[n];
for(int i=n/2;i>=1;i--)b[n]+=dfs(i);
return b[n];
}
int main()
{
for(int i=0;i<N;i++)b[i]=1;
cin>>x;
dfs(x);
cout<<b[x]<<endl;
return 0;
}
动态规划方法
当然对于记忆化搜索后的递归可还原为循环形式来减少回溯产生的时间。同时也是由规律得出的,因此也为动态规划的形式。
#include<iostream>
#include<cstring>
using namespace std;
const int N=1010;
int b[N],x;
int main()
{
cin>>x;
for(i=1;i<=x;i++)
{
b[i]++;
for(int j=i/2;j>=1;j--)b[i]+=b[j];
}
cout<<b[x]<<endl;
return 0;
}