算法竞赛入门第七章:迭代加深搜索

埃及分数问题,对于任意分数 ab ,都可以写成 1xixi,,

理论上来说,回溯法可以暴力解决问题,但是解答数的项数太多,广度和深度都是无限的。对于稍微复杂一点的数据,将不能解决问题。

(1) 而迭代加深搜索算法则是限制每一次搜索的深度最大为maxd,然后进行搜索。这样每次加深一层,只要解的深度不是太深,最终就可以得到结果。

(2) 另外在进行这种暴力枚举的时候,需要注意一点。每一次的节点扩展只要深一层都可能让时间上深一个量级。所以要尽可能的限制深度。在这个题目的环境下,如果深度是maxd,那么对于最后一项我们就可以不需要继续枚举,而是直接检查是否符合埃及数的定义(需要用到gcd化简);此外,如果不想使用gcd进行化简,还有另外一种方法,那就是当 1xiab, 但是这样有一点很容易错。这个检测只能在最后一层解答树执行,因为前面的节点是可以让 1xiab

(3) 另外利用节点深度的限制,可以进行剪枝来优化算法。(提高极大的效率)由于深度固定,每一层的分数不可能小于最优值的最小值,否则最终不能加到原分数。而这样的情况则在前一种剪枝里面就i剪掉了。所以这里不能利用最优值优化。另外还有一个get_first函数的实现,每一层的枚举需要从接近 ab 的最大分数值开始。

def gcd(a,b):
    return a if b==0 else gcd(b, a%b);
def get_first(a,b):
    c = b//a
    while c*a1
    return c
def solve_2(a,b):
    best = None
    def better(data):
        nonlocal best
        if best==None:return True
        else:
            for i in range(maxd,-1,-1):
                if data[i]!=best[i]:return data[i] < best[i]
        return False
    def dfs(a,b,beg,d):
        nonlocal best
        if d==maxd:
            if b%a:return
            data[d] = b//gcd(a,b)
            if better(data):best = data[::]
            return
        i = max(beg,get_first(a,b))
        while 1:
            if (maxd-d+1)*b<=a*i:break
            data[d] = i
            dfs(a*i-b,b*i,i+1,d+1)
            i+=1
    for maxd in range(1,100):
        best = None
        data = [0]*(maxd+1)
        dfs(a,b,get_first(a,b),0)
        if best:print(best);break
#print(get_first(15,225))
solve_2(495,499)

Uva11212

(1)bfs

def solve(n,lines):
    def bfs(data):
        nonlocal states,q
        q.append((data,0))
        while len(q):
            data,d = q.popleft()
            if data==tuple(range(1,n+1)):return d

            for i in range(1,n):
                for length in range(1,continues(data,i)):#n+1-i):
                #for length in range(1,n+1-i):
                    for j in range(i):
                        p1 = data[:j]
                        p2 = data[j:i]
                        p3 = data[i:i+length]
                        p4 = data[i+length:]
                        next = tuple(p1+p3+p2+p4)
                        if next not in states:
                            states.add(next)
                            q.append((next,d+1))
    states = set()
    q = deque()
    print(bfs(lines))

(2): IDA*算法,有以下几个要点:

(1): 利用启发函数,考虑不正确后继为h个,那么每次剪切时h最多减少3。

(2): 考虑状态的判重,需要将重复的状态记录,当某一个状态重复的时候,需要看它对应的深度和当前深度来进行处理。

def solve_2(n,lines):
    def count(data):
        cnt=1
        for i in range(n-1):
            if data[i]+1!=data[i+1]:cnt+=1
        return cnt
    def bfs(data,d):
        nonlocal states,ok
        if d==maxd:
            if data==tuple(range(1,n+1)):ok=True
            return
        for i in range(1,n):
            for length in range(1,n+1-i):
                for j in range(i):
                    p1 = data[:j]
                    p2 = data[j:i]
                    p3 = data[i:i+length]
                    p4 = data[i+length:]
                    next = tuple(p1+p3+p2+p4)
                    if next not in states or states[next]>d:
                        if 3*d+count(data)>3*maxd:continue
                        states[next]=d
                        bfs(next,d+1)
                        if ok:return
    states = {}
    for maxd in range(1,n):
        states = {}
        ok = False
        bfs(lines,0)
        print(maxd)
        if ok:print(maxd);break
#solve_2(8,[8,7,6,5,4,3,2,1])
test(solve_2,[8,[8,7,6,5,4,3,2,1]])

bfs 很容易发现由于迭代加深搜索前几次搜索速度非常的快。但是后面每一次的复杂度都急速上升。所以我们可以借鉴一下双向bfs的思路,从目标节点开始进行bfs,将第k(1,2,3..)层的节点存储起来,然后再进行IDA,这样就将迭代的深度限制在最多maxd-k层。经测试算法效率提高了几十倍。(对987654321从22s提高到0,77s.)

同理,利用双向bfs也可以大大加快直接bfs的速度。当然双向bfs略有不同,它是正反交替搜索的,这样的结果是深度被限制在 maxd2

def solve_2(n,lines):
    def count(data):
        cnt=1
        for i in range(n-1):
            if data[i]+1!=data[i+1]:cnt+=1
        return cnt
    def bfs(data,d,record=None):
        nonlocal states,ok,Record
        if d==maxd:
            if data==tuple(range(1,n+1)):ok=True;print('aa',maxd,data);return
            if data in Record:ok = 3;print('ss',maxd,data)
            if record!=None:record.add(data)
            return
        for i in range(1,n):
            for length in range(1,n+1-i):
                for j in range(i):
                    p1 = data[:j]
                    p2 = data[j:i]
                    p3 = data[i:i+length]
                    p4 = data[i+length:]
                    next = tuple(p1+p3+p2+p4)
                    if next not in states or states[next]>d:
                        if not record and 3*d+count(data)>3*(maxd+2):continue
                        states[next]=d
                        bfs(next,d+1,record)
                        if ok and record==None:return
    states = {}
    Record=set()
    maxd,ok=2,False
    bfs(list(range(1,n+1)),0,record=Record)
    for maxd in range(1,n):
        states = {}
        ok = False
        bfs(lines,0)
        if ok:print(maxd+ok-1);break

你可能感兴趣的:(ACM,常用算法)