天天看点

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左右!惊呼啊!主要是解法三的判断条件让程序去掉了很多没用的计算。

上一篇: XDOJ1144

继续阅读