题目描述
如果一个数 x 的约数和 y (不包括他本身)比他本身小,那么 x 可以变成 y,y 也可以变成 x。例如 4 可以变为 3,111 可以变为 7。限定所有数字变换在不超过 n 的正整数范围内进行,求不断进行数字变换且不出现重复数字的最多变换步数。
输入格式
输入一个正整数 n。
输出格式
输出不断进行数字变换且不出现重复数字的最多变换步数。
样例
样例输入
7
样例输出
3
样例说明
一种方案为 4→3→1→7。
数据范围与提示
对于 100% 的数据,1≤n≤50000。
——————————————————————————————————————————————————————————
看上去是一个数学相关的题目实际上是一个图论题。
题目要求每个点可以转换成约数和的条件是和的大小小于当前的数。所以每个点要么可以向前变,要么不在向前变。如果以约数为根那么就得到许多的树,也就是说,他们是一些森林,约数和是父亲。然后求最长的变换距离就是求最长连,树上DP就可以了。
——————————————————————————————————————————————————————————
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=50010; 4 int sum[maxn],dis1[maxn],dis2[maxn]; 5 int n; 6 void readint(int &x) 7 { 8 int f=1; 9 char c=getchar(); 10 for(;c<'0' || c>'9';c=getchar())if(c='-')f=-f; 11 x=0; 12 for(;c<='9' && c>='0';c=getchar())x=x*10+c-'0'; 13 x*=f; 14 } 15 void writeint(int x) 16 { 17 if(x<0) 18 { 19 putchar('-'); 20 x=-x; 21 } 22 if(x>9)writeint(x/10); 23 putchar(x%10+'0'); 24 } 25 void init() 26 { 27 for(int i=1;i<=n;++i) 28 for(int j=2;i*j<=n;++j) 29 sum[i*j]+=i; 30 } 31 void dp() 32 { 33 for(int i=n;i>0;--i) 34 { 35 int j=sum[i]; 36 if(j<i) 37 { 38 if(dis1[i]+1>dis1[j]) 39 { 40 dis2[j]=dis1[j]; 41 dis1[j]=dis1[i]+1; 42 } 43 else if(dis1[i]+1>dis2[j]) 44 dis2[j]=dis1[i]+1; 45 } 46 } 47 } 48 int main() 49 { 50 readint(n); 51 init(); 52 for(int i=1;i<n;++i)cerr<<i<<":"<<sum[i]<<endl; 53 dp(); 54 int ans=0; 55 for(int i=1;i<=n;++i)ans=ans>dis1[i]+dis2[i]?ans:dis1[i]+dis2[i]; 56 writeint(ans); 57 return 0; 58 }