算法竞赛入门第七章(2):枚举排列和子集

: 有几点要注意

(a): 首先对序列进行排序,然后枚举时候要跳过相同的元素。

(b): 由于存在相同的元素,因此枚举时要判断是否该元素已经全部用完了。

(c): 最后得到的结果是按照字典序从小到大的排列,同时相对以前直接传递参数保存剩下元素的方法来说:空间需求大大减少,但是时间消耗有所增加,当然还有很多可以优化的地方,比如统计元素的出现次数,可以提前计算好。

def do_permuations(P):
    def perm(P,A,cur):
        if cur==n:print(A);return
        item_bef = None
        for i in range(n):
            if P[i]==item_bef:item_bef = P[i];continue
            item_bef = P[i]
            c1,c2 = 0,0
            for j in range(cur):
                if A[j]==P[i]:c1+=1
            for j in range(n):
                if P[j]==P[i]:c2+=1
            if c1<c2:
                A[cur] = P[i]
                perm(P,A,cur+1)
    P = sorted(P)
    n = len(P)
    perm(P,[None]*n,0)
do_permuations([1,1,1,2])

求解下一个字典序的排列:方法是从右向左寻找,直到发现第一个非递增的元素,然后将这个元素以及其后面的元素进行一次排序,前面的元素排列不变,最后的加起来的结果就是next permutation.

def next_perm(this_p):
    n = len(this_p)
    ok=False
    for i in range(n-1,0,-1):
        if this_p[i-1]<this_p[i]:ok=True;break
    if not ok:return None
    ava = sorted(this_p[i-1:n])
    for x in ava:
        if x>this_p[i-1]:
            ava.remove(x)
            this_p[i-1] = x
            break
    this_p[i:n] = ava
    return this_p

: 相对与增量法,不需要进行定序就可以以极高的效率枚举完成。

def print_subset(array):
    def do():
        for i in range(0, 2**n):
            for j in range(n):
                #print('aa\n')
                if i & (1 << j):
                    print(array[j], end=' ')
            print()
    n = len(array)
    do()
print_subset([1, 2, 'sb'])

:需要生成子集的集合有重复元素。

为了简单,我们假设集合之间的元素可以进行排序,那么首先进行排序,然后每一次都枚举一个当前最小的元素作为子集的元素。最后要跳过相同的元素。在每添加一改新的元素之后都要输出新的子集。这样就保证不会有重复的子集。


def print_subset2(array):
    array.sort()
    def do(s,index):
        print(s)
        bef = None
        for i in range(index, n):
            if array[i] == bef:
                continue
            bef = array[i]
            s.append(array[i])
            do(s, i + 1)       
            s.remove(bef)

    n = len(array)
    do([], 0)
print_subset2([3,1, 2])

,如果集合中的元素不方便进行排序,但是是hashable的,那么就可对元素去重,先生成无重复集合的子集,然后将重复的元素添加到生成的子集之中。添加的标准是:添加之后,该元素的个数大于1。仔细想一下,这是为了防止生成重复子集。这是一种将问题转换到熟悉情况的思路。

def print_subset(array):
    def do():
        print()
        for i in range(1, 2**n):
            ans = []
            for j in range(n):
                if i & (1 << j):
                    ans.append(array_2[j])
            print(ans)
            if each in ans:
                for k in range(1, repeat[each] + 1):
                    if k + ans.count(each) > 1:
                        print(ans + [each] * (k))
    array_2 = list(set(array))
    repeat = {}
    for each in array:
        if each not in repeat:
            repeat[each] = 0
        else:
            repeat[each] += 1
    n = len(array_2)
    do()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值