我需要找到给定集合{1,2,3,...,N}中最大的子集,使得该子集中任意两个数之和都不能被给定数字K整除。由于N和K可能达到2 * 10 ^ 9,所以我需要一个非常快的算法。我只想到了一个时间复杂度为O(K)的算法,速度很慢。
我需要找到给定集合{1,2,3,...,N}中最大的子集,使得该子集中任意两个数之和都不能被给定数字K整除。由于N和K可能达到2 * 10 ^ 9,所以我需要一个非常快的算法。我只想到了一个时间复杂度为O(K)的算法,速度很慢。
首先,计算集合中所有元素 mod k 的值,并解决简单问题:找到给定集合中最大的子集大小,使得该子集中任意两个数的和不等于给定数字 K。
我将这个集合分成了两个集合(i 和 k-i),你不能同时选择集合 i 和集合 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
}
}
最后,您可以添加一个元素,该元素模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)之间的区别。 对于更好的解决方案,你可以基于模k对myset进行排序。
|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}的大小。@Constantine的公式给出|(6-1)/2|=|5/2|=2。上面的公式从前两行得到0,在第三行得到1,在最后一行得到2,得出了正确的答案。
公式是:
|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|
where |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|。 如果没有余数,我们可以添加一个数字(例如最后一个数字)。 如果余数 < |(k-1)/2|,我们获取最后一行中所有数字, 否则获取 |(K-1)/2|。
感谢注意特殊情况。 当 k>n 时, ost = 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
该解决方案的方法是:
代码: d是包含大小为n的初始数字集的数组。此代码的目标是找到d的最大子集的计数,使得没有两个整数的和可被2整除。
l是将数组d中的每个(元素)减少为(元素%k)并保存其出现频率的数组。
例如,l [1] 包含所有元素的频率 % k = 1