https://www.codechef.com/problems/MAXGCD
Chef有一个由N个整数组成的集合。如果子集具有两个或更多元素,Chef会调用此集合的子集。他将所有好的子集表示为S1,S2,S3,...,S2N-N-1。现在,他将每个良好子集Si的元素的GCD表示为Gi。 Chef希望找到最大的Gi。
输入
输入的第一行包含一个整数T,表示测试用例的数量。 T测试用例的描述如下。" 每个测试用例的第一行包含一个整数N,表示集合中的元素数。第二行包含N个空格分隔的整数A1,A2,...,AN,表示集合的元素。
输出
对于每个测试用例,输出最大Gi
我的解决方案:
这是我制作所有可能子集的代码:
vector< vector<int> > getAllSubsets(vector<int> set)
{
vector< vector<int> > subset;
vector<int> empty;
subset.push_back( empty );
for (int i = 0; i < set.size(); i++)
{
vector< vector<int> > subsetTemp = subset;
for (int j = 0; j < subsetTemp.size(); j++)
subsetTemp[j].push_back( set[i] );
for (int j = 0; j < subsetTemp.size(); j++)
subset.push_back( subsetTemp[j] );
}
return subset;
}
但是,我采用这种方法时会得到TLE。我在哪里错了?
答案 0 :(得分:2)
一个优化是您永远不需要考虑大于2个元素的子集。这是因为如果你添加另一个元素,GCD只能减少。
这会导致O(n^2)
算法。问题陈述说n
可以和100 000
一样大,所以我们需要做得更好。
问题还在于给定的值最多为500 000
,因此GCD不能超过此值。
让count[i] = how many times the value i appears in the array
。
然后我们可以应用与Sieve of Eratosthenes类似的内容:对于固定值v
,看看您是否可以找到两个v
的倍数(计数总和[multiple_of_v]&gt; 1 )。如果可以,那么你可以拥有v
的GCD。跟踪你能找到的最大值。
伪代码:
V = max(given array)
cnt[i] = how many times value i occurs in given array
for v = V down to 1:
num_multiples_v = 0
for j = v up to V:
num_multiples_v += cnt[j]
if num_multiples_v > 1: # TODO: break the inner loop when this is true
print v as solution
return
复杂性将是O(V log log V)
,这应该非常快。
答案 1 :(得分:0)
您不需要所有子集。
gcd的一些基本属性:
gcd(a,b) == gcd(b,a)
gcd(a,b) <= a
gcd(a,b) <= b
gcd(a,b,c) == gcd(a,gcd(b,c)) == gcd(gcd(a,b),c)
通过这个,很容易显示
gcd(a,b) >= gcd(a,b,c) >= gcd(a,b,c,d)...
表示任何自然数a,b,c,d。
您想要找到具有最大值的(其中一个)子集。 GCD。根据上面的规则,这个子集中的一个具有恰好两个元素(假设整个集合具有至少两个元素)。因此,第一个优化是抛弃子集生成并使其类似
max = 0
for all set elements "a"
{
for all set elements "b"
{
if(gcd(a,b) > max)
max = gcd(a,b)
}
}
如果仍然不够,首先将集合表格从最大元素排序到最小元素,对于在循环中计算的每个gcd,删除小于计算值的每个集合元素。