最大子集,其中没有两个可被K整除的和

时间:2012-12-22 09:23:19

标签: c++ algorithm formula

我被给予{1,2,3,...,N}集合。我必须找到给定集合的子集的最大大小,以便来自子集的任何2个数字的总和不能被给定的数字K整除.N和K可以达到2 * 10 ^ 9所以我需要一个非常快的算法。我只提出了复杂度为O(K)的算法,这种算法很慢。

5 个答案:

答案 0 :(得分:5)

首先计算所有设置元素mod k。并解决简单问题: 找到给定集合的子集的最大大小,使得来自子集的任何2个数字的总和不等于给定数量K. 我把这个集合分为两组(i和k-i)你不能同时选择set(i)和set(k-i)。

int myset[]
int modclass[k]

for(int i=0; i< size of myset ;i++)
{
    modclass[(myset[i] mod k)] ++;
}

选择

for(int i=0; i< k/2 ;i++)
{ 
    if (modclass[i] > modclass[k-i])
    {
        choose all of the set elements that the element mod k equal i
    }
    else
    {
        choose all of the set elements that the element mod k equal k-i
    }
}

最后你可以添加一个元素,因为元素mod k等于0或k / 2。

该解决方案具有复杂度O(K)的算法。

你可以用动态数组改进这个想法:

for(int i=0; i< size of myset ;i++)
{
    x= myset[i] mod k;
    set=false;
    for(int j=0; j< size of newset ;j++)
    {
        if(newset[j][1]==x or newset[j][2]==x)
        {
            if (x < k/2)
            {
                newset[j][1]++;
                set=true;
            }
            else
            {
                newset[j][2]++;
                set=true;
            }
        }
    }
    if(set==false)
    {
        if (x < k/2)
        {
            newset.add(1,0);
        }
        else
        {
            newset.add(0,1);
        }
    }
}

现在您可以选择复杂度为O(myset.count)的算法。并且您的算法超过O(myset.count),因为您需要O(myset.count)来读取您的集合。 这个解决方案的复杂性是O(myset.count ^ 2),你可以选择算法依赖你的输入。比较O(myset.count ^ 2)和o(k)。 为了更好的解决方案,您可以根据mod k对myset进行排序。

答案 1 :(得分:4)

我假设某些N的数字总是1到N.

考虑第一个N-(N mod K)数字。 K个连续数的形式层(N / K)序列,从0到K-1减少mod K.对于每个组,必须丢弃楼层(K / 2)以具有减少模K,其是另一个楼层子集(K / 2)的否定模K.您可以从每组K个连续数字中保持上限(K / 2)。

现在考虑剩下的N mod K数。他们从1开始减少mod K.我没有计算出确切的限制,但是如果N mod K小于大约K / 2,你将能够保留所有这些。如果没有,你将能够保持它们的第一个上限(K / 2)。

=============================================== ===========================

我相信这里的概念是正确的,但我还没有弄清楚所有的细节。

=============================================== ===========================

以下是我对问题和答案的分析。接下来是| x |是楼(x)。这个解决方案与@ Constantine的答案类似,但在少数情况下有所不同。

考虑第一个K * | N / K |元素。它们由| N / K |组成重复减少模数K。

一般来说,我们可以包括| N / K | K模k的元素受以下限制:

如果(k + k)%K为零,我们只能包含一个k模k的元素。就是k = 0和k =(K / 2)%K的情况,这只能发生在甚至K.

这意味着我们得到| N / K | * |(K-1)/ 2 |重复的元素。

我们需要纠正遗漏的元素。如果N> = K,我们需要为0 mod K元素添加1。如果K是偶数且N> = K / 2,我们还需要为(K / 2)%K元素添加1。

最后,如果M(N)!= 0,我们需要添加重复元素的部分或完整副本,min(N%K,|(K-1)/ 2 |)。

最终的公式是:

|N/K| * |(K-1)/2| +
(N>=K ? 1 : 0) +
((N>=K/2 && (K%2)==0) ? 1 : 0) +
min(N%K,|(K-1)/2|)

这与@ Constantine的版本不同,在某些情况下甚至涉及K.例如,考虑N = 4,K = 6。正确答案是3,集合的大小{1,2,3}。 @康斯坦丁的公式给出了|(6-1)/ 2 | = | 5/2 | = 2.上面的公式对于前两行中的每一行都是0,从第三行得到1,从最后一行得到2,得到正确答案。

答案 2 :(得分:3)

公式

|N/K| * |(K-1)/2| + ost 

ost =
if n<k:
  ost =0
else if n%k ==0 :
  ost =1    
else if n%k < |(K-1)/2| :
  ost = n%k
else:
  ost = |(K-1)/2|

其中| a / b | 例如| 9/2 | = 4 | 7/2 | = 3

例子n = 30,k = 7;

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

1 2 3 | 4 | 5 6 7. - 是第一行。  8 9 10 | 11 | 12 13 14 - 第二行 如果我们在每行中获得前3个数字,我们可能得到该子集的大小。我们也可以从(7 14 28)

中添加一个数字

获得前3个数字(1 2 3)是数字|(k-1)/ 2 | 。 这条线的数量是| n / k | 。   如果没有残留,我们可以添加一个数字(例如最后一个数字)。  如果残留&lt; |(K-1)/ 2 |我们得到最后一行的所有数字 否则得到|(K-1)/ 2 |。

感谢异常案例。 如果k> n

,则ost = 0

答案 3 :(得分:0)

n,k=(raw_input().split(' '))
n=int(n)
k=int(k)
l=[0 for x in range(k)]
d=[int(x) for x in raw_input().split(' ')]
flag=0
for x in d:
   l[x%k]=l[x%k]+1

sum=0

if l[0]!=0:
    sum+=1 
if (k%2==0):
    sum+=1


if k==1:
    print 1
elif k==2:
    print 2 
else:       
    i=1
    j=k-1
    while i<j:
        sum=sum+(l[i] if l[i]>=l[j] else l[j])
        i=i+1
        j=j-1
     print sum

答案 4 :(得分:0)

这是对ABRAR TYAGI和amin k&#39的解释。

此解决方案的方法是:

  • 使用K个桶创建一个数组L,并将所有元素分组 输入数组D进入K桶。每个桶L [i]包含D的元素,使得(元素%K)= i。
  • 所有可被K分割的元素都在L [0]中。所以 只有这些元素中的一个(如果有的话)可以属于我们的最终(最大) 子集。这些元素中的任何两个的总和可被K整除。
  • 如果我们将L [i]中的元素添加到L [K-i]中的元素,那么该和可以被K整除。因此我们可以只从这些桶中的一个添加元素到 我们的最终设定。我们挑选最大的桶。

代码: d是包含大小为n的初始数字集的数组。这段代码的目标是找到d的最大子集的计数,使得没有两个整数的总和可以被2整除。

l是一个包含k个整数的数组。我们的想法是将数组d中的每个(元素)减少为(元素%k),并将它们出现的频率保存在数组l中。

例如,l [1]包含所有元素的频率%k = 1

我们知道1 +(k-1)%k = 0因此必须丢弃l [1]或l [k-1]以满足没有两个数字%k的总和应该为0的标准。

但是由于我们需要d的最大子集,我们选择较大的l [1]和l [k-1]

我们循环遍历数组l,使得(i = 1; i <= k / 2&amp;&amp; i&lt; k-i; i ++)并执行上述步骤。

有两个异常值。 l [0]组中任意两个数的总和%k = 0.因此,如果l [0]为非零,则加1。

如果k为偶数,则循环不处理i = k / 2,并使用与上述相同的逻辑将计数递增1。