数字转换 信息学奥赛一本通 树形dp
Posted 行码棋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数字转换 信息学奥赛一本通 树形dp相关的知识,希望对你有一定的参考价值。
题目:
如果一个数
x
x
x 的约数和
y
y
y (不包括他本身)比他本身小,那么
x
x
x 可以变成
y
y
y,
y
y
y 也可以变成
x
x
x。例如
4
4
4 可以变为
3
3
3,
1
1
1 可以变为
7
7
7。限定所有数字变换在不超过
n
n
n 的正整数范围内进行,求不断进行数字变换且不出现重复数字的最多变换步数。
思路:
- 首先对可以相互转换的数进行连接,可以看作是树中的一条边,权值为1
- 如何找到可以相互转换的数:
用类似筛法的思想,因为要求一个数的所有的约数和(除去本身),使用普通的方法复杂度太大,我们采用这种方法,复杂度近似 n l o g n nlogn nlogn.枚举每个数,找这个数的倍数,看这个数是哪些数的约数。 - 然后dfs求当前节点向下的最大长度。同时记录最大值和次大值,并不断更新答案
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+5;
int n;
int sum[N],st[N];
int e[N],ne[N],h[N],idx;
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;i=ne[i])
{
int v = e[i];
int t = dfs(v) + 1;
//记录最大值和次大值
if(t >= d1) d2 = d1,d1=t;
else if(t>d2) d2 = t;
}
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);
for(int i=2;i<=n;i++)
if(sum[i] < i)
{
add(sum[i],i);
st[i] = 1;//标记其他节点
}
//找到未标记的节点,从根节点开始dfs
for(int i=1;i<=n;i++)
if(!st[i])
dfs(i);
cout<<ans<<'\\n';
return 0;
}
以上是关于数字转换 信息学奥赛一本通 树形dp的主要内容,如果未能解决你的问题,请参考以下文章