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

本文探讨了半数集问题,给出了问题描述、算法设计和C++实现。通过对自然数n按照特定规则产生半数集,计算集合元素个数。递归算法的时间复杂度为O(n^n),存在重复计算。文章提供了优化后的代码,减少了计算时间。

问题描述:给定一个自然数n,由自然数n开始可以依次产生半数集set(n)中的数如下:
                   (1)n属于set(n)
                   (2)在n的左边添加一个自然数,但该数不能超过最近添加的数的一半
                   (3)按此规则,直到不能添加为止
                   例如:set(6)={6,16,26,36,126,136}=6

算法设计:对于给定的自然数n,计算set(n)中的元素个数

输入:自然数n

输出:元素个数和元素内容

算法思想:以6为例。按照规则,新加入的数字小于等于6的一半——3。所以6进行一轮,添加了1,2,3三个数字组合为16,26,36。接下来发现只有26和36可以按照规则继续加数字。

根据规则,对每一个数n最多可以添加n/2个数字且不重复,如果把每一个自然数看作树的节点,那么对一棵树上的节点来说都有n/2个子节点。符合分治策略的使用条件。有如下递归式:

根据树的性质,我们可以采用一维数组来遍历set(n)中的元素。

6
61
621
631

代码如下

#include<iostream>
#include<cstring>
using namespace std;
int a[10^5];

int  set(int n, int i) {
	a[i] = n;
	for (int j = i; j >= 1; j--) {  
		cout << a[j];
	}
	cout << endl;
	int ans = 1;
	for (int k = 1; k <= n / 2; k++) {
		ans += set(k, i + 1);
	}
	return ans;
}

 递归的时间复杂度为O(n^n)。

通过递归式,我们发现包含许多重复计算和重复元素。重复计算浪费了很多计算时间。

对此,针对重复计算问题,优化代码如下:

//递归优化
int set_new(int n, int i)
{
	a[i] = n;
	for (int j = i; j >= 1; j--) {  //遍历数组(从后往前)
		cout << a[j];
	}
	cout << endl;
	int ans = 1;
	if (b[n] > 0)return b[n];
	for (int k = 1; k <= n / 2; k++) {
		ans += set(k, i + 1);
	}
	b[n] = ans;
	return ans;
}

对于半数单集问题:

//半数单集
int set_new_new(int n, int i)
{
	a[i] = n;
	for (int j = i; j >= 1; j--) {  //遍历数组(从后往前)
		cout << a[j];
	}
	cout << endl;
	int ans = 1;
	if (b[n] > 0)return b[n];
	for (int k = 1; k <= n / 2; k++) {
		ans += set(k, i + 1);
		if ((i > 10) && (2 * (i / 10) <= i))
			ans -= a[i / 10];
	}
	b[n] = ans;
	return ans;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值