acwing 1075 数字转换 (树形DP)

题面

在这里插入图片描述

题解

  1. 首先,对于小于n的每个数,我们可以确定它的约数之和(不包括自己)是固定的,就像4的约数之和一定是3,不可能是其他的,那么我们就可以将2-n的每个数的约数之和求出sum[i],对于sum[i]<i 的我们连一条sum[i]---->i 的边(因为对于每个i,sum[i]是唯一确定的),也就是说每个儿子都有唯一一个父节点,那么我们最终就会构成森林(多课树),题中找最长的变换步骤就转换成了求树的最长直径
  1. 为什么不从1开始 ,因为1除本身外约数之和就是0,题中要求最小为1,所以1是不符合要求的
  1. 如何找一树的最长直径,我们可以依次枚举每个点,找这个点最长和次长距离,然后相加即可 ,具体可看树的最长路径,这里就不详细讲解了

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 5e4 + 10;

int n;
int h[N], e[N], ne[N], idx;
int sum[N];   //约数之和
bool is[N];   //是否已经用过
int ans;


void add(int a, int b) {
    
    
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

int dfs(int 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() {
    
    

    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);
        }
        is[i] = true;
    }

    for (int i = 1; i <= n; i++) {
    
    
        if (!is[i]) {
    
       //说明是父节点
            dfs(i);
        }
    }

    cout << ans << endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44791484/article/details/114745264
今日推荐