(烧脑)奇怪数 - C语言 - 回溯

回复此贴:https://bbs.csdn.net/topics/394896625

一、题目

奇怪数为这样一个整数 @wowpH
①:除了自身以外所有因子之和大于这个数本身(首先必须是盈数)
②:除了自身以外所有因子的集合,没有任何一个子集中所有数的和等于这个数本身

例如:70就是一个奇怪数,求1000以内的第二个奇怪数
70的除自身外的因子有:1,2,5,7,10,14,35。
它们的和为:74 > 70,所以70是盈数。
不存在一个子集的和为70(或4),所以它是奇怪数。


二、分析思路

核心部分分两个函数:一个用于判断是否是盈数,一个用于判断是否存在子集和为自身。@pfdvnah

1、用数组保存所有除自身外的因子
2、for 循环查找所有因子。循环到平方根即可。一趟循环可以找出两个因子。
3、注意,如果数自身是平方数,那么循环里面就多加了一次。循环后面要减掉。
4、是盈数返回 1,不是返回 0

子集和通过回溯计算。更好的是 DP 实现,怕新手看不懂,所以直接用回溯解决。实际上思路差不多。
每个数有两种状态,加到子集中,不加到子集中。@wowpH
不断地这样选择,直到所有数都选择完后,看看还剩余多少,剩的刚好为 0 的话,说明有子集的和为 num,即不是奇怪数。


三、C语言代码

/*****************************************************************
	功能:计算并输出区间[LFET,RIGHT]中所有的奇怪数
	作者:wowpH pfdvnah
	日期:2019年10月31日10:10:03
	链接:https://blog.csdn.net/pfdvnah/article/details/102832648
*****************************************************************/
#include <stdio.h>
#include <math.h>
#include <string.h>

#define TRUE 1
#define FALSE 0
#define MAX_FACTOR_NUM 1000
#define LEFT 71
#define RIGHT 1000

int strangeNumber(int);// 判断是否是奇怪数

int sum;// 因子的和
int factor[MAX_FACTOR_NUM];// 保存因子
int factorNum;// 因子个数。@wowpH
int abundantNumber(int);// 判断是否是盈数

int back(int, int);// 回溯判断是否有子集的和是num

int main(void) {
	for (int i = LEFT; i <= RIGHT; ++i) {
		if (TRUE == strangeNumber(i)) {
			printf("%d\n", i);
		}
	}
	return 0;
}

int strangeNumber(int num) {
	if (FALSE == abundantNumber(num)) {
		return FALSE;// 不是盈数,即不是奇怪数
	}
	if (FALSE == back(0, num)) {
		return FALSE;// 存在子集的和为num,即不是奇怪数
	}
	return TRUE;
}

int abundantNumber(int num) {
	// 初始化。@pfdvnah
	memset(factor, 0, sizeof(factor));
	factorNum = 0;
	factor[factorNum++] = 1;// 1肯定是因子
	sum = 1;
	int max = (int)sqrt(num);
	// 循环找到num的所有因子
	for (int i = 2; i <= max; ++i) {
		if (0 == num % i) {
			factor[factorNum++] = i;
			factor[factorNum++] = num / i;
			sum += i + num / i;
		}
	}
	// num是开方数,那么循环里面max加了两遍,要减掉多余的
	if (max * max == num) {
		--factorNum;
		sum -= max;
	}
	return sum <= num ? FALSE : TRUE;
}

int back(int index, int left) {
	if (index >= factorNum) {
		return 0 == left ? FALSE : TRUE;
	}
	if (FALSE == back(index + 1, left)) {// 不选择第index个数
		return FALSE;
	}
	// 选择第index个数,剩余的减去当前数
	if (FALSE == back(index + 1, left - factor[index])) {
		return FALSE;
	}
	return TRUE;
}

四、相关链接

题目来源:https://bbs.csdn.net/topics/394896625
原文链接:https://blog.csdn.net/pfdvnah/article/details/102832648
盈数_百度百科:https://baike.baidu.com/item/%E7%9B%88%E6%95%B0/2567294?fr=aladdin


- End - wowpH - pfdvnah -
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值