问题描述:
给定一个自然数n,由n 开始可以依次产生半数集set(n)中的数如下。
(1) n∈set(n);
(2) 在n 的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;
(3) 按此规则进行处理,直到不能再添加自然数为止。
例如,set(6)={6,16,26,126,36,136}。半数集set(6)中有6 个元素。
注意半数集是多重集。
对于给定的自然数n,计算半数集set(n)中的元素个数。
半数集set(n)中元素个数的求解是个递归的过程。设set(n)中的元素个数为f(n),则显然有递归表达式:
f(n)=1+∑f(i),i=1,2……n/2
// 递归算法
long comp(int n)
{
long sum = 1; // 记录半数集中产生的元素个数
if (n <= 1)
return 1;
for (int i=1; i<=n/2; i++)
{
sum += comp(i);
}
return sum;
}
// 非递归实现
int n = 12;
int[] s = new int[n+1];
int comp(int n, int[] s)
{
int sum = 1;
// 已经计算过
if (s[n] > 0)
return s[n];
for (int i=1; i<=n/2; i++)
{
sum += comp(i);
}
s[n] = sum; // 数n的半数集元素个数
return sum;
}
半数单集问题:
半数单集类似半数集,区别在于:半数集是多重集,而半数单集不是多重集,即集合中已有的元素不再添加
到集合中。例如 24。 24->1224 , 24->224->1224。
对于n<=200的情况。在0 < n < 201时,0 < n/2 <= 100,在计算时,可能产生重复的元素是2位数。一个两位数
x重复产生的条件是:其个位数y = x % 10的半数集中已经产生了x(个位数是十位数的至少两倍), 因此
x /10 <= y / 2,或 2(x / 10) <= x % 10。
// 非递归实现
int n = 2; // 2448 1248
int[] s = new int[n+1];
int comp(int n, int[] s)
{
int sum = 1;
// 已经计算过
if (s[n] > 0)
return s[n];
for (int i=1; i<=n/2; i++)
{
sum += comp(i);
if (i > 10 && (2*(i/10)<=(i%10)))
sum -= s[i/10];
}
s[n] = sum; // 数n的半数集元素个数
return sum;
}