算法设计与分析之半数集问题(C++)

算法设计与分析之半数集问题

问题描述

要求找出具有下列性质的数的个数(包含输入的自然数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=1n/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);
    }
}

本博客其他文章推荐

关于C++整型数组自动初始化为零的情况记录

算法设计与分析之线性时间选择

算法设计与分析之分治策略练习(下)

算法设计与分析之分治策略练习(上)

算法设计与分析之分治策略

扫描二维码关注公众号,回复: 12049609 查看本文章

算法设计与分析之递归算法练习(下)

算法设计与分析之递归算法练习(上)

猜你喜欢

转载自blog.csdn.net/L333333333/article/details/102671667