如果一个数 x 的约数之和 y(不包括他本身)比他本身小,那么 x 可以变成 y,y 也可以变成 x。
例如,4 可以变为 3,1 可以变为 7。
限定所有数字变换在不超过 n 的正整数范围内进行,求不断进行数字变换且不出现重复数字的最多变换步数。
输入格式
输入一个正整数 n。
输出格式
输出不断进行数字变换且不出现重复数字的最多变换步数。
数据范围
1≤n≤50000
输入样例:
7
输出样例:
3
样例解释
一种方案为:4→3→1→7。
代码:
/*
1、如果一个数 x 的约数之和 y(不包括他本身)比他本身小,则x与y连一条边,问题转化为求树的直径问题
2、由于只用到y到x的边,因此只需要连一条从y到x的有向边即可,但必须从当前树的最小的数(树根)开始向下递归
3、由于题目可能会存在多棵树,因此需要st[]数组标记该点是否被遍历过
(其中这里只有一棵以1为根结点的树,只需要dfs(1)即可,原因是啥没证明出来)
按照这个循环,每个数的最后都会走到质数,然后质数约数之和就会走到1。(22可以继续约数之和2111,得到13)。
不知道这样证明能不能行得通
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int sum[N];
int h[N], ne[N], e[N], idx;
bool st[N];
int ans = 0;
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int dfs(int u)
{
st[u] = 1;
int d1 = 0, d2 = 0;
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (st[j])
continue;
int dist = dfs(j) + 1;
if (dist > d1)
d2 = d1, d1 = dist;
else if (dist > d2)
d2 = dist;
}
ans = max(ans, d1 + d2);
return d1;
}
int main()
{
memset(h, -1, sizeof(h));
cin >> n;
for (int i = 1; i <= n; i++)//类似于埃氏筛
for (int j = 2; j <= n / i; j++)
sum[i * j] += i;
for (int i = 2; i <= n; i++)
{
if (sum[i] < i)
{
add(sum[i], i);
add(i, sum[i]);
}
}
dfs(1);
cout << ans;
return 0;
}