问题描述
要求找出具有下列性质的数的个数(包含输入的自然数n):
先输入一个自然数n(n<=500),然后对此自然数按照如下方法进行处理:
- 不作任何处理
- 在它的左边加上一个自然数,但该自然数不能超过原数字的一半
- 加上数后,继续按此规则进行处理,直到不能再加自然数为止
如输入6,则有
6
16
26
126
36
136
输出6
问题分析
我们很容易得求半数集数量的递推式:
f ( n ) = 1 + ∑ i = 1 n / 2 f ( i ) f(n) = 1 + \sum_{i=1}^{n/2}f(i) f(n)=1+i=1∑n/2f(i)
我们使用递推式进行递归计算,为了避免重复计算我们采用记忆式的搜索。
算法实现
#include <iostream>
#include <cstring>
#define N 500
using namespace std;
int a[N] = {
0}; //存储计算过的数据 初始化所有值为零
//求半数集算法
int halfSet(int);
int main()
{
int n;
cout<<"请输入n:";
cin>>n;
cout<<n<<"的半数集元素数量为:"<<halfSet(n)<<endl;
return 0;
}
int halfSet(int n){
if(a[n] > 0) return a[n];
int ret = 1;
for(int i=1;i<=n/2;i++){
ret = ret + halfSet(i);
}
a[n] = ret;
return ret;
}
但上面的算法存在纰漏,会有重复子问题,比如24的半数集中有两个1224,生成方式不同但结果相同,我们需要再改进一下算法。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <set>
using namespace std;
//求半数集算法 改进 无重复子问题
void halfSet(int,set<string>&,string);
int main()
{
int n;
set<string> s;
char *str = new char[3];
cout<<"请输入n:";
cin>>n;
if(n > 500){
cout<<"数据超范围!";
exit(0);
}
halfSet(n,s,string(itoa(n,str,10)));
cout<<n<<"的半数集元素数量为:"<<s.size()+1<<endl;
return 0;
}
void halfSet(int n,set<string> &s,string str){
char *strs = new char[3];
string mStr;
for(int i=1;i<=n/2;i++){
mStr = string(itoa(i,strs,10)) + str;
s.insert(mStr);
halfSet(i,s,mStr);
}
}
本博客其他文章推荐
扫描二维码关注公众号,回复:
12049609 查看本文章