【问题标题】:How do I exactly solve this GCD?我该如何解决这个 GCD?
【发布时间】:2015-10-11 13:12:19
【问题描述】:

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

我的解决方案:

  1. 我生成给定集合的所有可能子集。
  2. 我使用欧几里得算法计算每组的 GCD
  3. 我试图找到所有这些中的最大值。

这是我制作所有可能子集的代码:

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。我哪里错了?

【问题讨论】:

  • 对于这类问题,您通常需要一点点数学知识。假设我告诉你 GCD (48, 72) = 24。对于任意 x 的 GCD (48, 72, x) 或任意 x, y 的 GCD (48, 72, x, y, z) 你能说什么,z?回答完这个问题,思考一下其中的含义。有什么理由去检查所有个好的集合吗?
  • 我得出的结论是,如果 GCD(x,y) 为 1,那么任何包含 (x,y) 的超集的 GCD 都将为 1。但这对我有什么用处?我真的不能在任何地方使用它。
  • @gnasher729,我该怎么做呢?
  • 这只是影响的一小部分。想多了?

标签: c++ algorithm


【解决方案1】:

一项优化是您永远不需要考虑大于 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 的两个倍数(count 的总和[multiple_of_v] > 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),应该非常快。

【讨论】:

  • 如果问题是找到一组所有可能子集的 GCD,那么,你的方法会奏效吗?我的意思是,删除 return 语句?
  • 虽然你的方法看起来很棒,但我不能以有效的方式找到给定数字集的所有可能子集吗?
  • @Raj 具有 n 个元素的集合具有 n! 可能的子集。步数少于n! 就无法获得n! 结果,这是慢。
  • @Raj 这是一个完全不同的问题,我不确定你的意思是“找到一组所有可能子集的 GCD”。这简化为只找到给定集合的 GCD(因为子集只会重复其中的一部分),您可以在 O(n) 中进行操作。如果你的意思是找到每个子集的 GCD,你不能比O(2^n) 做得更好,因为你至少必须打印/返回那么多值。无论如何,这是一个不同的问题。如果您有后续问题,请发布另一个问题。如果需要,您可以将其链接为相关的,但它应该是自包含的。
  • @IVlad,那我该怎么做呢?如果你能告诉我这将是一个很大的帮助。谢谢! :)
【解决方案2】:

您不需要所有子集。

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,删除每个小于计算值的集合元素。

【讨论】:

  • 虽然这肯定会有所帮助,但这还不够,因为n 可以达到100 000。排序是启发式的。可能有一个测试用例会破坏它。
猜你喜欢
  • 2021-01-09
  • 1970-01-01
  • 1970-01-01
  • 2016-07-21
  • 2021-06-29
  • 2020-03-25
  • 2016-09-09
  • 2020-02-14
  • 2017-06-06
相关资源
最近更新 更多