一、算法实现题:
1、问题描述:给定一个自然数n,有n开始可以一次产生半数集set(n)中的数如下:
(1)n∈set(n);
(2)在n的左边加上一个自然数,但该自然数不能超过最近添加数的一半;
(3)按此规则进行处理,直到不能在添加自然数为止。
例:set(6) = {6,16,26,126,36,136}。半数集set(6)中有6个元素
2、编程任务:对于给定的自然数n,计算半数集set(n)中的元素个数。
3、数据输入:输入数据由文件名为input.txt的文本文件提供。每个文件只有一行,给出整数n(0<n<1000)。
4、结果输出: 将计算结果输出到文件output.txt。输出文件只有一行,给出半数集set(n)中的元素个数。
二、解题思路
通过备忘录算法求解
通过递归算法的思想可知道,在每一次求n的半数集问题,都要连续不断的求前面1到n/2的子半数集问题。那么就可以为每个子问题建立一个记录项,用a[n]来保存子问题的解。在该记录项存入一个特殊的值,表示改之问题尚未求解,在求解过程中,对于每个待求的子问题,首先查看其相应的记录项。若记录项中存储的值是初始化的值,那么就表示改子问题是第一次求解。此时计算改子问题,并保存到相应的记录项中。若记录项中的值不是初始化,则表示改子问题已经被解决,记录项中的值就是改子问题的解。
用递归算法求解
对于一个给定的整数n,按照其半数集规则,在该数的左边只能添加小等于n/2的自然数。既在n的左边可以添加1n,2n,3n,4n,……,((n/2)-1)n,(n/2)n。那么就可以把整数n的半数集个数看成求1到n/2的半数集的个数之和,然后加上1(代表整数n本身)。而(n/2)的半数集问题也可以依照上述的规律递归求出。这样就可以把一个n的问题划分为n/2个子问题。
设set(n)为求n的半数集函数,那么n的半数集个数=set(1)+set(2)+……+set((n/2)-1)+set(n/2)。
三、算法描述
//方法一 备忘录
#include <stdio.h>
int a[1000],b; // 把a[1000]做为子问题的备忘录。
void get(int n)
{ int i,j,k;
for(i=b+1; i <= n; i++)
{ k = i/2;
for(a[i]=j=1; j <= k; j++)
a[i] = a[i] + a[j];
}
b = n;
}
void main()
{ int n;
b = a[0] = a[1] = 1;
while(scanf("%d",&n)!=EOF)
{ if(n > b)// 当所提供的整数小于备忘录中所存的数,
//就直接求解
get(n);
printf("the result is %d/n",a[n]);
}
}
//方法二 递归算法
#include <fstream.h>
#include <iostream.h>
ifstream fin("input.txt");
ofstream fout("output.txt");
int set(int m)
{ int i;
unsigned long count=1;
for(i=1;i <=m/2;i++)
{
count+=set(i);
}
return count;
}
void main()// 主函数
{ int n;
fin >> n;
fout<<set(n)<<endl;
fin.close();
fout.close();
}