ACM解题总结——HihoCoder1187

题目来源:
    HihoCoder1187

题目要求:
    题目要求给定一个数字n,在所有不大于n的数字中,找出因数最多的数字。如果有多个数字有相同数目的因数,那么输出数值最小的一个。

解答:
    
本题的难度在于数据的范围很大。如果采用暴力的方式解答的话,一定会超时。因此需要考虑一些高效率的算法。
    本题的解法基于以下的定理:对于一个数,要么它是一个素数,要么它可以分解为多个素数的乘积形式,并且如果不考虑因数的顺序,这种分解方式一定是唯一的(证明不会 )。即任何数字的分解质因数的结果都是唯一的。于是对于任意的数字n,都可以写成如下的形式:
     
    其中:p1 p2 ... pm都是素数。由于等式右边是m个数字相乘,因此取出上面右边式子其中的一部分得到的结果一定是n的因数。
    由于每一个数字的分解质因数的结果是唯一的,因此,不同的素数的组合乘积也一定是不同的,而上式中右边的数字均为素数。因此基于这样的结论,就可以得到数字n的因数的数目C(n)为:
    
     这可以理解为一个构造素数乘积组合的过程,首先考虑p1,可以不选择p1,也可以选择1个p1,选择2个...选择l1个,因此共有(l1+1)中选择方法。其他素数的选择方式也类似,因此不同的素数乘积组合就有:(l1+1)(l2+1)....(lm+1)种。而每一种都确定n的唯一的一个因数。
    到这里,本题的思路就比较清晰了,枚举素数的乘积的组合,然后在保证乘积不大于给定的n的时候,找到因数数目最多的数据。类似于背包问题的思路,首先考虑最小的素数2,考虑不选择2,选择1个,选择2个....直到2的k次方大于n为止。当确定了2的一个选择策略时,再递归考虑下一个素数3,以此类推。当递归结束是,一定能得到本题的解。
    但是,这样的解法同样会超时,因此还需要进行一步重要的剪枝。这里要注意:如果有多个数字有最多的因数数目,这时要选择数值最小的一个。因此本题的最优解一定满足下面的条件:
    假设题目中的最优解是x,把x分解质因数的结果如下:
         
如果p1 p2 ... ps 是升序排列的话,那么就可以得到这样的结论:
            k1≥k2≥....≥ks
即,这些素数的指数是降序排列的。证明很简单,交换上式中任意两个素数因子的指数得到x',那么根据上面的计算公式,x'和x拥有的因数的数目是相同的,但x'一定是大于x的,x'就一定不是我们要找的解。
    这样在进行背包枚举时,每进入一轮递归,只需要枚举比上一轮递归时选择的指数小的数字即可,计算效率也可以大大提升。

输入输出格式:
    输入:输入仅有一行,给出数字n
    输出:输出也仅有一行,输出不大于n的数字中因数数目最多的数字,有多个解话,输出最小的一个。 

数据范围:
     0≤n≤10000000000000000 (10^16)

程序代码:
/****************************************************/
/* File        : hiho_week_89.cpp                   */
/* Author      : Zhang Yufei                        */
/* Date        : 2016-03-16                         */
/* Description : HihoCoder ACM program. (submit:g++)*/
/****************************************************/

/*
 * Update log:
 *		Create by Zhang Yufei in 2016-03-16.
 */
 
#include<stdio.h>

/*
 * The prime numbers list.
 */
int prime[13] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};

// The input.
long long n;

// Record the result to output.
long long result;

// Record the number of devisors.
long long max_devisors;

/*
 * This function computes the number of devisiors of the 
 * given n.
 * Parameters:
 *		@num: The number n to opearte.
 *		@dice: The dice of the last prime.
 *		@index: The current index of prime number.
 *		@devisors: The current number of devisors.
 * Returns:
 *		None.
 */ 
void compute(long long num, int dice, int index, long long devisors);

/*
 * The main program.
 */
int main(void) {

	scanf("%lld", &n);
	
	result = -1;
	max_devisors = -1;

	compute(1, 64, 0, 1);
	
	printf("%lld\n", result);
	
	return 0;	
}

/*
 * This function computes the number of devisiors of the 
 * given n.
 * Parameters:
 *		@num: The number n to opearte.
 *		@dice: The dice of the last prime.
 *		@index: The current index of prime number.
 *		@devisors: The current number of devisors.
 * Returns:
 *		None.
 */ 
void compute(long long num, int dice, int index, long long devisors) {

	if(max_devisors == -1 || max_devisors < devisors) {
		max_devisors = devisors;
		result = num;		
	}
	if(max_devisors == devisors && num < result) {
		result = num;
	}
	
	if(index == 13) {
		return;
	}
	
	int i = 1;
	long long c = prime[index];
	while(c * num <= n && i <= dice) {
		compute(c * num, i, index + 1, devisors * (i + 1));
		i++;
		c *= prime[index];
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值