AcWing 1075 数字转换

题目描述:

如果一个数 x 的约数之和 y(不包括他本身)比他本身小,那么 x 可以变成 y,y 也可以变成 x。

例如,4 可以变为 3,1可以变为 7。

限定所有数字变换在不超过 n 的正整数范围内进行,求不断进行数字变换且不出现重复数字的最多变换步数。

输入格式

输入一个正整数 n。

输出格式

输出不断进行数字变换且不出现重复数字的最多变换步数。

数据范围

1≤n≤50000

输入样例:

7

输出样例:

3

样例解释

一种方案为:4→3→1→7。

分析:

首先,一个数字可以变成另外一个数字,就算是这两个数字之间存在一条边,当x的约数之和y小于x时,x与y之间边存在边。一个数的约数之和是唯一的,但一个数可以是很多数的约数之和,所以一个数的父节点就是这个数的约数之和。首先考虑如何求出1-n中所有数的约数之和,由于要在不超过n的正整数范围内变换,所以不需要求1的约数之和。可以采用类似于筛法的形式去求约数之和,将i的若干倍都加到其倍数的约数之和上即可。

for(int i = 1;i <= n / 2;i++){
        for(int j = 2;j <= n / i;j++){
            s[i * j] += i;
        }
}

然后仿照树的最长路径那题对树做DFS,求出最长路径就是本题的最多变换步数了,因为不能确定1到n中所有的数都是连通的,所以需要多次进行DFS来遍历这个森林。这里的边也可以设置为双向的,但是由于是从小到大进行DFS,相当于从根节点开始遍历,就不需要增加孩子节点指向父节点的边来降低DFS的速度了。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 50005;
int s[N],h[N],e[N],ne[N],idx,ans;
bool vis[N];
void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int dfs(int u){
    vis[u] = true;
    int d1 = 0,d2 = 0;
    for(int i = h[u];~i;i = ne[i]){
        int j = e[i];
        int d = dfs(j) + 1;
        if(d >= d1) d2 = d1,d1 = d;
        else if(d > d2) d2 = d;
    }
    ans = max(ans,d1 + d2);
    return d1;
}
int main(){
    int n;
    cin>>n;
    memset(h,-1,sizeof h);
    memset(vis,false,sizeof vis);
    for(int i = 1;i <= n / 2;i++){
        for(int j = 2;j <= n / i;j++){
            s[i * j] += i;
        }
    }
    for(int i = 2;i <= n;i++){
        if(s[i] < i)    add(s[i],i);
    }
    for(int i = 1;i <= n;i++){
        if(!vis[i]) dfs(i);
    }
    cout<<ans<<endl;
    return 0;
}
发布了311 篇原创文章 · 获赞 31 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/104402279
今日推荐