寻找最大排列问题

现在八个人去电影院看电影,他们现在都有了自己的位置,但是他们其中有的满意,有的不满意。他们想做的座位如下图:

图中a,b,c,d,e,f,g,h都表示座位,也表示座位上的人,箭头指向他们想坐的座位。


那么问题是:找到一个子集,使得满意的人数达到最多的那个方案。


分析如下:

1.如果各个人指向的座位不同,那么整个集合本身就是结果了。(大家喜欢的座位各不相同,那还争什么呀,大家交换就是了)

2.那么至少要有两个人指向同一个座位(这样问题才有意思,有人争座位!a和b打起来了,他们有一个肯定不在结果集中!那么要淘汰谁呢?!)

3.淘汰那个没有人指向自己座位的人!(比如b,不淘汰b的话,a就没地方去了,那么接下来c也没地方去了。哎。后果很严重!)


假设:

a=0,b=1,c=2,d=3,e=4,f=5,g=6,h=7


M=[2,2,0,5,3,5,7,4]#表示他们想去的位置

一个简单的递归的朴素实现方案:

def naive_max_perm(M, A=None):
    if A is None: # The elt. set not supplied?
        A = set(range(len(M))) # A = {0, 1, ... , n-1}
    if len(A) == 1: return A # Base case -- single-elt. A
    B = set(M[i] for i in A) # The "pointed to" elements
    C = A - B # "Not pointed to" elements
    if C: # Any useless elements?
        A.remove(C.pop()) # Remove one of them
        return naive_max_perm(M, A) # Solve remaining problem
    return A # All useful -- return all

运行:

>>> naive_max_perm(M)
{0, 2, 5}
时间复杂度平方级。因为B的生成需要线性时间。

另一种实现:

引入计数的思想:

为各元素设置一个计数器,这样一来,每当有指向座位x的人被淘汰时,我们就只需递减该座位的计数器,并在x的计数器为0时,将编号为x的人和座位一同出局即可。


def max_perm(M):
    n = len(M) # How many elements?
    A = set(range(n)) # A = {0, 1, ... , n-1}
    count = [0]*n # C[i] == 0 for i in A
    for i in M: # All that are "pointed to"
        count[i] += 1 # Increment "point count"
    Q = [i for i in A if count[i] == 0] # Useless elements
    while Q: # While useless elts. left...
        i = Q.pop() # Get one
        A.remove(i) # Remove it
        j = M[i] # Who's it pointing to?
        count[j] -= 1 # Not anymore...
        if count[j] == 0: # Is j useless now?
            Q.append(j) # Then deal w/it next
    return A # Return useful elts.

算法复杂度:线性级时间。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值