AcWing1075. 数字转换(树形DP)题解

题目传送门

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

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

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

输入格式

输入一个正整数 n。

输出格式

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

数据范围

1≤n≤50000

输入样例:

7

输出样例:

3

样例解释

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

题解:

树形DP:

首先预处理出每个数的约数之和,我们可以对每一个合法的路径进行建边

最后就可以转化为求树的最长路径问题

#include
#include
using namespace std;
const int N = 50010;
int n;
int h[N], e[N], ne[N], idx;
int sum[N];
bool vis[N];
int ans;
void add(int a, int b)
{
     
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u)   //求以u为根节点的最长路径
{
     
   int d1 = 0, d2 = 0;  //记录最长路径和次长路径
   for(int i = h[u]; i != -1; 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()
{
     
    cin >> n;
    for(int i = 1; i <= n; i++)     //预处理出每个数的约数之和
        for(int j = 2; j <= n / i; j++)
            sum[i * j] += i;  
    memset(h, -1, sizeof h); //1的约数之和为0, 所以从2开始建边
    for(int i = 2; i <= n; i++){
        
        if(i > sum[i]){
        //对合法路径进行建边
            add(sum[i], i);
            vis[i] = true;  //记录树根
        }
    }
    for(int i = 1; i <= n; i++){
     
        if(!vis[i]){
        //如果该点是树根
            dfs(i);
        }
    }
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(动态规划,动态规划,算法)