题目描述:
求500万以内的所有亲和数。
如果两个数a和b的所有真因数之和等于b,b的所有真因数之和等于a,则称a,b是一对亲和数。
例如:220和284,1184和1210,2620和2924.
需要注意的是关于真因数的定义:出去整数本身之外的所有因子都称作该整数的真因子,这里是包括1的。
#include<stdio.h>
int sum[5000010]; //为防越界
int main()
{
int i, j;
int N=5000000;
for (i = 0; i <= N; i++)
sum[i] = 1; //1是所有数的真因数所以全部置1
for (i = 2; i + i <= N; i++) //预处理,预处理是logN(调和级数)*N。
//@litaoye:调和级数1/2 + 1/3 + 1/4......的和近似为ln(n),
//因此O(n *(1/2 + 1/3 + 1/4......)) = O(n * ln(n)) = O(N*log(N))。
{
//5000000以下最大的真因数是不超过它的一半的
j = i + i; //因为真因数,所以不能算本身,所以从它的2倍开始
while (j <= N)
{
//将所有i的倍数的位置上加i
sum[j] += i;
j += i;
}
}
for (i = 220; i <= N; i++) //扫描,O(N)。
{
// 一次遍历,因为知道最小是220和284因此从220开始
if (sum[i] > i && sum[i] <= N && sum[sum[i]] == i)
{
//去重,不越界,满足亲和
printf("%d %d\n",i,sum[i]);
}
}
return 0;
}
这种解法的基本过程是:首先,我们先把所有sum都初始化为1,这个可以理解;然后,我们遍历i的所有倍数,这些数由于是i的倍数,所以它们的真因子一定会包含i,故有了sum[j]+=i;这样的语句,这里需要注意的是,i没有必要遍历到N,因为最大的真因数是不会超过N/2的,这样我们便可以得到所有数的真因子的和sum数组。这里还有一个细节需要解释一下,就是有的数会存在多个真因子,那么每次遍历的时候都会加上的,不会有遗漏。例如:6这个整数的真因子有2和3,那么在遍历2的倍数时,会有sum[6]+2的操作,在遍历3的倍数的时候,又会有sum[6]+3的操作。对于那些只有一个真因子的数,只需要加一次,而对于质数,就不要加了。
计算机解迷宫时,通常用的是"试探和回溯"的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止,如果所有可能的通路都试探过,还是不能走到终点,那就说明该迷宫不存在从起点到终点的通道。
1.从入口进入迷宫之后,不管在迷宫的哪一个位置上,都是先往东走,如果走得通就继续往东走,如果在某个位置上往东走不通的话,就依次试探往南、往西和往北方向,从一个走得通的方向继续往前直到出口为止;
2.如果在某个位置上四个方向都走不通的话,就退回到前一个位置,换一个方向再试,如果这个位置已经没有方向可试了就再退一步,如果所有已经走过的位置的四个方向都试探过了,一直退到起始点都没有走通,那就说明这个迷宫根本不通;