XDOJ最喜欢的数字

Description

         zyf最喜欢的数字是1!所以他经常会使用一些手段,把一些非1的数字变成1,并为此得意不已。他会且仅会的两种手段是:
1.把某个数m除以某个质数p——当然p必须能整除这个数,即m=m/p
2.把某个数m减1,即m=m-1
有一天他突发奇想,想把[a,b]区间中所有的数一个一个地变成1,这是一个巨大的无聊的工程,所以他想知道他最少得花多少操作才能达到目的。

Input
  输入包含多组数据(1000组数据),EOF结束。
  每组数据以两个整数开头:a,b(0<a<=b<=100000),意义如题意描述。
Output
  每组数据输出一行,最少操作数。
Sample Input
2 3
3 5
11 12
Sample Output
2
4
3
 
问题分析:
要求出把a,b之间所有数变成1的最少步骤,肯定得计算出每个数字变成1的最少步骤。那么如何求出一个数字通过题中两种运算变成1的最小步骤呢?
分析到这,我们脑中肯定展现出一个树状结构出来,每个节点是当前的数字,而他可以有未知个孩子节点,包括减一操作以及除以的所有的素因子的操作。于是我们感觉到问题的复杂,求一个数字变成1的方法尚且这么复杂,还让求出最少步骤。即使算出来了,也很费时啊。
引用小平爷爷的一句话:没有调查就没有发言权!
1.其实认真想一下,一个数字的素因子是有限的,据2*3*5*7*11*13<100000<2*3*5*7*11*13*17可以判断100000以内的数字最多有6个素因子。所以我们的分支没有那么多。
2.不要忘了任何一个素数只需一步就可以变成1。100000以内大概有一万个素数,用素数筛选法计算出素数后,我们已经解决了一万个数字了。而且后面假设用递归或者动态规划也很容易的可以使用剪枝了。
3.计算n需要多少步骤时,其实是比较n-1,n/p……(p为素因子)中最小步骤的然后加一即可。
到此大概算法已经有了。
解法一:果断可以递归吗,最朴素的算法没有优化,需要10秒左右才可以算完100000以内的所有数。
解法二:在素数筛选后把素数弄到素数表中存起来,减少判断素数的时间,同时去掉函数的递归调用,因为可以从小的数往大的数算,也即是迭代式的。效率提高到3秒。
int search_deep(int num){	
	if(data[num] != 0){
		return data[num];
	} else {
		int temp = data[num - 1] + 1;
		int i = 0;

/* 解法一	
		for(i = num / 2; i > 1; i--){
			if(!prime[i] && num % i == 0 && search_deep(num / i) + 1 < temp){
			//	temp = search_deep(num / i) + 1;
				temp = data[num / i] + 1;
			}
		}
*/
// 解法二
		for(i = 1; prime_array[i] <= num / 2; i++){
			if(temp <= 2)
				break;
			if(num % prime_array[i] == 0 && data[num / prime_array[i]] + 1 < temp){
				temp = data[num / prime_array[i]] + 1;
			}
		}
	
		return temp;
	}
	return 0;
}

解法二做了大量优化已经达到了3秒的时间效率,可是还是太慢,其实主要原因的总是从最小素数开始判断是否为素因子,感觉接近1的速度就会慢下来。如果能从最大的素因子开始判断就更好了,可是似乎没什么解决办法。罢了。
 
解法三:类似于素数筛选法,著名的素数筛选法应该都知道不用解释。那么也可以利用这个思想来解决这个问题。如果数n变成1的步骤为x,那么n乘以任何一个素数的步骤都是x+1。这个思想似乎是真正的迭代,先解决小问题,再解决大问题,速度要高很多!代码如下:
void generate(){
	prime[1] = 1;
	prime[2] = 0;
	prime[3] = 0;
	int i = 0;
	int j = 0;
	int k = 1;
	for(i = 2; i < 100001; i++){
		if(prime[i] == 0){
			data[i] = 1;
			for(j = 2; i * j < 100001; j++){
				prime[i * j] = 1;
			}

			prime_array[k++] = i;
		}
	}

	for(i = 2; i <= 100000; i++){
		if(data[i - 1] + 1 < data[i])
			data[i] = data[i - 1] + 1;

		for(j = 1; j < k && 1.0 * i * prime_array[j] < 100001; j++){
			if(data[i] + 1 < data[i * prime_array[j]] || data[i * prime_array[j]] == 0){
				data[i * prime_array[j]] = data[i] + 1;
			}
		}
	}
}
解法三的执行效率一下提高到了10ms左右!惊呼啊!主要是解法三的判断条件让程序去掉了很多没用的计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值