- 题意:给出n个人的能力值ai,需将其从大到小进行排序若挑选出来的两人gcd(ai, aj) != 1, 则该两人可以组合在一个队伍中(并且相邻),一个队伍中不相邻的两人可以不用满足gcd != 1的条件,这n个人能组合在一个队伍里最多能有几个人,若人数小于3输出-1,否则输出队伍最多的人数.
- 思路:Dp + 质因数分解
- 解析:若gcd(ai, aj) != 1说明ai与aj必然有相同的质因数;
- dp[i]表示到第i个人为止,该队伍最多能有的人数
- m[x]表示质因数x在1-(i-1)的能力值序列中,一些子序列并且这些子序列结尾的能力值质因数包含x的最长子序列长度 例如:20(2, 2, 5) 、18(2, 3, 3)、 9(3, 3)
那么从i = 1开始, m[2] = 1, m[5] = 1;
i = 2,m[2] = 2, m[3] = 2;
i = 3, m[3] = 2 + 1 = 3.
其实这里有一个很巧妙的关系,20与18的相同质因数是2,而18与9的相同质因数是3,而gcd(20, 9) = 1,所以他们两个不能直接相连,但是可以通过18将其二者相连接,所m[x]代表的是1 - (i-1)个数挑选出满足条件的子序列并且子序列结尾的值的质因素包含x的最大长度(语文功底太差了,写的好费劲~~~)
- dp[i] = max(dp[i], m[x] + 1);
- 最后需要将m[x]更新,m[x] = dp[i];
- ps:这题的dp听大佬讲后都挺难理解的,就大概写了一下题解,搜了一下网上也没见有题解(真不懂怎么写~~)
- 代码:
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
const int N = 3e5 + 5;
const int M = 1e5 + 5;
int prime[M], vis[M];
map<int, int> m;
vector<int> fac[N];
int dp[N], a[N];
int n, cnt = 0;
bool cmp(int a, int b)
{
return a > b;
}
void init() //素数打表
{
vis[0] = vis[1] = 1;
for(int i = 2; i <= 1e5; i++)
{
if(!vis[i]) prime[++cnt] = i;
for(int j = 1; j <= cnt && i * prime[j] <= 1e5; j++)
{
vis[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
void getfac() //质因数分解
{
for(int i = 1; i <= n; i++)
{
int val = a[i];
int num = 1;
while(val > 1)
{
if(!vis[val]) //val为素数
{
fac[i].push_back(val);
val = 1;
break;
}
while(val % prime[num] == 0)
{
fac[i].push_back(prime[num]);
val /= prime[num];
}
num ++;
}
}
}
int main()
{
init();
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n, cmp);
getfac();
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < fac[i].size(); j++)
{
int x = fac[i][j];
dp[i] = max(dp[i], m[x] + 1);
}
for(int j = 0; j < fac[i].size(); j++)
{
int x = fac[i][j];
m[x] = dp[i];
}
}
int res = -1;
for(int i = 1; i <= n; i++)
res = max(res, dp[i]);
if(res > 3) cout << res << endl;
else cout << "-1" << endl;
return 0;
}