每天算法一丁点(3)--递归算法应用:半数集

半数集问题

题意:给定一个自然数n,由n开始依次产生半数集 set(n),set(n) 的定义如下:

  1. n 是 set(n) 中的元素
  2. 在 n 的左边添加一个自然数,但该自然数不能超过最近添加的数的一半
  3. 按此规律添加,直到不能添加自然数为止

例如:set(6) = {6,16,26,126,36,136}

求半数集中元素的个数

分析:给定一个数n,那么在其前可以添加的数有从 1 到  m = n/2 。在这 m 个数中,m 前可以添加的数又有从 1 到 s = m/2 。

因此,用递归+循环解决。循环 1 到 m 。 递归每一层前面可添加的数: n -> n/2 => m -> m/2

代码如下:

#include <iostream>
using namespace std;

// 输入n,计算其半数集中有多少数
// eag: set(6)={6,16,26,36,126,136} 有6个
// ps:半数集:元素每次生成都不大于最近添加的数的一半

int half_set(int n)
{
    int sum = 1;
    if (n > 1)
    {
        for (int i = 1; i <= n / 2; i++)
        {
            sum += half_set(i);
        }
    }
    return sum;
}

int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        printf("%d的半数集中有%d个数\n",n,half_set(n));
    }
}

上面的代码有重复性的计算,比如 set(8)={8,18,28,38,48,128,138,148,248,1248}, 其中 8 前面可以添加 4 的半数集。 4 的半数集是确定的。因此利用数组来记住计算结果,可以避免不必要的计算:

代码优化如下:

#include <iostream>
using namespace std;
#define N 1100
int result[N];

int half_set(int n){
    int sum=1;
    if(result[n])
        return result[n];
    for(int i=1; i<=n/2; i++)
        sum += half_set(i);
    result[n] = sum;                    //记录已经计算过的数,避免重复计算;
    return sum;                         //比如8前面有1、2、3、4.这四个数前面可添加的数是都是固定的。
}
int main(){
    int n;
    while(scanf("%d",&n)){
        memset(result,0,sizeof(result));
        printf("%d的半数集元素个数为:%d\n",n,half_set(n));
    }
}

原文地址:https://www.cnblogs.com/yinniora/p/12109187.html

时间: 2024-07-29 22:46:41

每天算法一丁点(3)--递归算法应用:半数集的相关文章

每天算法一丁点(2)--递归算法应用:鸭子知多少

应用题:鸭子知多少? 题意:有个人赶鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又两只,这样经过n个村子后还剩2只鸭子,问他出发时共赶多少只鸭子? 分析:一共有s只鸭子,那经过第一个村子就剩下 remain1 = s/2-2 只鸭子,经过第二个村子就剩下 remain2 = remain1/2-2 只鸭子..以此类推,经过n个村子,remain(n) = remain(n-1)/2-2 只鸭子,根据题意,最后剩2只. remain(n)=2. 倒着推回去可得, remian(n-1) =

算法研讨会-含有回溯的递归算法设计探讨

目录 含有回溯的递归程序设计 目录 回溯 1.1 概念 1.2 基本思路 1.3 问题举例 1.4 算法设计 1.5 PTA编程题 含有回溯的递归程序设计 目录 回溯 1.1 概念 递归是一种算法结构.技巧,而回溯是一种算法思想. 本质上是一种枚举思想,采用深度优先策略来枚举所有可能解,并且服从一定的择优条件. 遵循设定好的择优条件不断深入试探,最终达到目标,但是在试探过程中,若发现当前情况不是最优或者一定无法达到目标时,将返回到上一个状态,对上一个状态进行重新选择后,继续深入试探. 1.2 基

常用的算法思想总结

对于计算机科学而言,算法是一个非常重要的概念.它是程序设计的灵魂,是将实际问题同解决该问题的计算机程序建立起联系的桥梁.接下来,我们来看看一些常用的算法思想. (一)穷举法思想 穷举法,又称为强力法.它是一种最为直接,实现最为简单,同时又最为耗时的一种解决实际问题的算法思想. 基本思想:在可能的解空间中穷举出每一种可能的解,并对每一个可能解进行判断,从中得到问题的答案. 使用穷举法思想解决实际问题,最关键的步骤是划定问题的解空间,并在该解空间中一一枚举每一个可能的解.这里有两点需要注意,一是解空

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

ACM 算法实现

实验一 统计数字问题 1.问题描述:一本书的页码从自然数1 开始顺序编码直到自然数n.书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0.例如,第6 页用数字6 表示,而不是06 或006 等.数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1, 2,…,9.2.题目分析:考虑由0,1,2,…,9组成的所有n位数.从n个0到n个9共有个n位数,在这些n位数中,0,1,2,…,9每个数字使用次数相同,设为. 满足如下递归式:由此可知,.据此,可从低位向高位进行

算法导论-动态规划-钢条切割

动态规划通常用于解决最优化问题,在这类问题中,通过做出一组选择来达到最优解.在做出每个选择的同时,通常会生成与原问题形式相同的子问题.当多于一个选择子集都生成相同的子问题时,动态规划技术通常就会很有效,其关键技术就是对每个这样的子问题都保存其解,当其重复出现时即可避免重复求解. 钢条切割问题 Serling公司购买长钢条,将其切割为短钢条出售.切割工序本身没有成本支出.公司管理层希望知道最佳的切割方案.假定我们知道Serling公司出售一段长为i英寸的钢条的价格为pi(i=1,2,…,单位为美元

算法前戏 递归 二分查找 列表查找

一.递归 概念: 函数直接或者间接的调用自身算法的过程,则该函数称为递归函数.在计算机编写程序中,递归算法对解决一大类问题是十分有效的. 特点: ①递归就是在过程或者函数里调用自身. ②在使用递归策略时,必须有一个明显的结束条件,称为递归出口.问题规模相比上次递归有所减少, ③递归算法解题通常显得很简洁,但递归算法解题的效率较低.所以一般不倡导使用递归算法设计程序. ④在递归调用的过程当中系统的每一层的返回点.局部变量等开辟了栈来存储.递归函数次数过多容易造成栈溢出等. 所以一般不倡导用递归算法

递归算法核心思想

递归是一种函数或方法中调用自身的编程技术,递归思想在于把大问题分解为小问题,进一步分解为更小的问题,直到每个小问题可以解决为止,也就是说,递归就是 用与自己相似但规模较小的问题来描述自己. 递归算法的三个特性: 1,求解规模为n的问题可以转化为一个或多个结构相同规模较小的的问题,然后从这些较小的问题可以方便构造出大问题的解 2,递归调用的次数必须是有限的 3,递归必定有结束条件来终止递归, 递归的算法执行过程为递推和递归两个阶段.在递推阶段,将规模为n的问题的求解递推到比原来规模小的问题的求解,

动态规划算法(java)

一.动态规划算法 众所周知,递归算法时间复杂度很高为(2^n),而动态规划算法也能够解决此类问题,动态规划的算法的时间复杂度为(n^2).动态规划算法是以空间置换时间的解决方式,一开始理解起来可能比较困,自己画画也许明白了很多. 二.动态规划算法分析 先举个例子: {7,0,0,0,0},{3,8,0,0,0},{8,1,0,0,0},{2,7,4,4,0},{4,5,2,6,5} 这个二维数组,求一下,顶层到底层,只能通过两端来相加的最大值(也就是说这棵树的最长路径). 分析: (1)第一步: