3-sum 问题
给定一个整数 x 和一个由整数构成的集合 S,从 S 中找一 个由三个元素构成的子集,该子集中的三个元素之和必须等于 x。使用穷举法列举 S 的所有 由三个元素构成的子集,逐个检查其是否满足条件。
穷举法
最容易想到的就是穷举法了,事实上,我们很容易写出subset-sum 问题(求出全部集合S的任意长度的和为给定常数x的子集)的算法
# input S , x
n = len(S)
for i in range(1, 2 ** n):
T = []
for j in range(n):
if (i >> j) % 2 == 1: T.append(S[j])
if sum(T) == x:
print(T, end = ' ')
复杂度为 O ( a n ) O(a^n) O(an)
稍微修改一下,容易得到3-sum 问题。
# input S , x.
n = len(S)
for i in range(1, 2 ** n):
T = []
for j in range(n):
if (i >> j) % 2 == 1: T.append(S[j])
if len(T) == 3: break
if sum(T) == x:
print(T, end = ' ')
# [73, 6, 5, 51] [21, 77, 5, 32]
当然,没有奇葩会这么干(冗余操作太多了,我们生成了
1
∼
2
n
−
1
1\sim2^n-1
1∼2n−1的所有数,然后选取其中二进制有三个1的数,并检验集合中对应数字的和是否为x……)
正常的穷举法,应该是三个for循环
# input S , x.
n = len(S)
T = []
for i in range(0, n):
for j in range(i+1, n):
for k in range(j+1, n):
if S[i]+S[j]+S[k] = x:
T.append([S[i],S[j],S[k]])
print(T)
时间复杂度为 O ( n 3 ) O(n^3) O(n3)
算法优化
我们尝试对穷举法进行优化。
假定存在一个子集 {a, b, c} 满足 x = a + b + c。原问题可以转换为另一个问 题:对于 S 中的任意两个元素 b 和 c,在 S − {b, c} 中查找一个等于 x − b − c 的元素。为了提高查找的效率,可以先将所有元素按照从小到大的顺序排序,然后使用二分查找。如此下来,时间复杂度为 O ( n 2 log n ) O(n^2\log n) O(n2logn)
def bisect(list,target,j):
low, high = j,len(list)-1
while low<=high:
mid = (low + high)//2
if list[mid] == target :
return True
elif list[mid] < target:
low = mid + 1
elif list[mid] > target:
high = mid-1
return False
S = input("set:").split(",")
S = list(map(int,S))
x = int(input("target:"))
S.sort()
n = len(S)
T = []
for i in range(0, n):
for j in range(i+1, n):
if bisect(S,x-S[i]-S[j],j):
T.append([S[i],S[j],x-S[i]-S[j]])
print(T)