coprime_sequence(互质序列)
问题描述
顾名思义,互质序列是满足序列元素的 gcd 为 1 的序列。比如[1,2,3],
[4,7,8],都是互质序列。 [3,6,9]不是互质序列。现在并不要求你找出一个互质
序列,那样太简单了!真正的问题描述是:给定一个序列,删除其中一个元素使
得剩下元素的 gcd 最大,输出这个 gcd。
★数据输入
输入第一行为一个正整数 n。 第二行为 n 个正整数 ai(1<=ai<=10^9)。
80%的数据 2<=n<=1000.
100%的数据 2<=n<=100000.
★数据输出
输出一个正整数,表示最大的 gcd。
输入示例 | 输出示例 |
3 1 1 1 |
1 |
输入示例 | 输出示例 |
5 2 2 2 3 2 |
2 |
输入示例 | 输出示例 |
4 1 2 4 8 |
2 |
★Hint
最大公因数缩写是 gcd。 gcd(a,b,c)=gcd(a,gcd(b,c)).
解题思路
暴力算法小规模可以,但是复杂度达到O(n^2),大规模数据超时。因此必须采用更好的算法。
期初,我的想法是将从左到右算过的数据存下来,即开一个数组,将第1个数的gcd(本身)存在第1个位置,将第1~2个数的gcd存在第2个位置,1~3个数的gcd存在第3个位置,以此类推。
而其中第1~n个数的gcd可由gcd( 1~(n-1)的gcd , 第n个数 )求得 是递推的过程,复杂度O(n)。
但仅仅这样只比暴力节省一半时间。因此,仿照前面的过程,引入从右到左的gcd计算
开等长数组left[] right[] ,将 从左到右 和 从右到左 的gcd递推计算结果分别存入left[] 与right[]
那么除掉下标为 i 的数,其他数的为gcd( left[i-1] , right[i+1]) 首尾做特殊判断
这样遍历一遍,就能找到gcd_max
code
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int p[100002] = {0}; 5 int left[100002] = {0}; 6 int right[100002] = {0}; 7 8 void swap(int &a, int &b) 9 { 10 a ^= b; 11 b ^= a; 12 a ^= b; 13 } 14 15 int Getgcd(int n, int m) 16 { 17 if (n < m) swap(n, m); 18 return n%m == 0 ? m : Getgcd(m, n%m); 19 } 20 21 int main() 22 { 23 // freopen("test.txt","r",stdin); 24 int n, i, j; 25 scanf("%d", &n); 26 // int *p = (int *)malloc(sizeof(int)*n); 27 for (i = 0; i < n; i++) 28 scanf("%d", p + i); 29 30 int gcd = -1; 31 for(i=0; i<n; i++) //for(i=0;i<n-1;i++) 32 { 33 if(i==0) 34 gcd = p[0]; 35 else 36 gcd = Getgcd(p[i],left[i-1]); 37 left[i] = gcd; 38 } 39 for(i=n-1; i>=0; i--) //for(i=n-1;i>0;i--) 40 { 41 if(i==n-1) 42 gcd = p[n-1]; 43 else 44 gcd = Getgcd(p[i],right[i+1]); 45 right[i] = gcd; 46 } 47 48 int maxgcd = -1; 49 for(i=0; i<n; i++) // except p[i] 50 { 51 if(i==0) 52 gcd = right[1]; 53 else if(i==n-1) 54 gcd = left[n-2]; 55 else 56 gcd = Getgcd(left[i-1],right[i+1]); 57 if(gcd>maxgcd) maxgcd = gcd; 58 } 59 printf("%d ",maxgcd); 60 61 // free(p); 62 return 0; 63 }