传送门:hdu5902。
题意还比较简单,就是做的时候有点懵逼,既然它让求任意三个数的gcd,因为数据量较小就可以遍历,每两个数取一次gcd,将gcd用数组标记出来,因为题中说明还要再把取出来的三个数的最大gcd放回去,假设某一次放回去的为a,a,下一次取出来的为a,a,x,此处x为任意的输入的数,(这里可能会有疑问说如果取得三个数都是之前求得的gcd呢,假设取出来的x,y,z,都是之前求得的gcd,假设放回去的gcd “m"是由x,y求得的,那么就相当于x和求得y的两个数(p,q)再加上z做了两次gcd(x,p,z)和(x,q,z)求得m,这样回溯到底就相当于括号外面的做法)则再放回去的可能为gcd(a,a)或者gcd(a,x),所以每一次产生了新的gcd后就要将被标记出来的数(上几步求出来的gcd)和输入的数重新做一遍gcd,最后没有新的gcd了就停止,最后输出的时候再注意一下格式就行了。
这样做的缺点是时间复杂度很高,在本题中数据量小还可以,不过也达到了795MS,数据量稍微大一点的话就很难了,希望大神能有更好的解法。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int gcd(int n,int m)//辗转相除求最大公约数
{
int r;
while(m)
{
r=n%m;
n=m;
m=r;
}
return n;
}
int num[505],book[1001];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
}
memset(book,0,sizeof(book));
for(int i=0;i<n-1;i++)//两两求gcd
for(int j=i+1;j<n;j++)
{
book[gcd(num[i],num[j])]=1;
}
int flag=1;//标记是否有新的gcd产生
int len=1;
while(flag&&len<n-2)
{
flag=0;
len++;
for(int i=1;i<=1000;i++)//将之前求出来的gcd和输入的数两两取再gcd
for(int j=0;j<n;j++)
{
if(book[i]&&!book[gcd(i,num[j])])
{
flag=1;
book[gcd(i,num[j])]=1;
}
}
}
flag=1;
for(int i=0;i<=1000;i++)//输出注意格式
{
if(book[i])
{
if(flag)
{
printf("%d",i);
flag=0;
}
else
printf(" %d",i);
}
}
putchar('\n');
}
return 0;
}