假设我们知道了前n-1个数的最长上升子序列,这时加入第n个数a[n],如果想知道以第n个数结尾的最长上升子序列有多长,那么我们就需要找到前n-1个数中与a[n]有公约数的所有a[i],也就是有公因子,并从他们中的取最大值maxLen+1就是以a[n]结尾最长上升长度。因此我们可以将a[n]分解,比如第二个样例中,对于10, 10 = 2*5 ,然后找到前面包含因子2和5数有,2,5,6,8等。。。
关键就在于怎么快速找到前面包含2和5因子结尾的最长长度,因此要存储的状态就变成了fac_dp[i]以质因数i的倍数的结尾的最长子序列多长
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define maxn 100005
int n;
int a[maxn];
int fac_dp[maxn]; //fac_dp[i]记录包含质因数i的倍数结尾最长子序列多长
int isprime[maxn]; //判断素数
int prime[maxn]; //存储素数因子
int fac[maxn];
int main()
{
for(int i = 0; i < maxn; i++) isprime[i] = 1;
isprime[0]=0; isprime[1]=1;
int cur = 0;
for(int i = 2; i < maxn; i++) //预处理找到所有素数因子
if(isprime[i]){
prime[cur++] = i;
for(int j = 2*i; j < maxn; j+=i)
isprime[j] = 0;
}
while(scanf("%d", &n)!=EOF){
for(int i = 1; i <= n; i++)
scanf("%d", a+i);
memset(fac_dp, 0, sizeof(fac_dp)); //初始化为0
int ans = 0;
for(int i = 1; i <= n; i++){
if(isprime[a[i]]){ //如果a[i]为素数,前面必然没有与他有公约数的数,所以长度直接为0
ans = max(++fac_dp[a[i]], ans);
continue;
}
int cnt = 0;
int maxi = 0;
int num = a[i];
for(int j = 0; j < cur && num>1; j++) //将a[i]质因数分解
if(num%prime[j]==0){
fac[cnt++] = prime[j];
maxi = max(fac_dp[prime[j]]+1, maxi);
while(num%prime[j]==0) num/=prime[j];
}
ans = max(ans, maxi);
for(int j = 0; j < cnt; j++) //更新a[i]所包含的所有素数因子的fac_dp值
fac_dp[fac[j]] = max(fac_dp[fac[j]], maxi);
}
printf("%d\n", ans);
}
return 0;
}