算法竞赛入门第七章:回溯与路径寻找

原创 2016年08月29日 20:49:15

PrimeRing,Uva524:

基本思路:直接回溯即可。注意一个细节每一个长度为n的环对应n种排列,因此输出要求固定第一个值为1。

Primes = set([2, 3, 5, 7,  11, 13, 17, 19, 23, 29, 31])


def is_prime(x): return x in Primes
vis = [0] * (16 + 2)
C = [0] * (16 + 2)


def prime_ringe(cur, n):
    if cur == n:
        if is_prime(C[0] + C[n - 1]):
            print(C[0:n])
        return
    for i in range(2, n + 1):
        if not vis[i] and (cur == 0 or is_prime(i + C[cur - 1])):
            C[cur], vis[i] = i, 1
            prime_ringe(cur + 1, n)
            vis[i] = 0


def process():
    while 1:
        n = input()
        if n == 0:
            break
        C[0] = 1
        prime_ringe(1, int(n))

Krpton Factor Uva129:首先从左到右确定每一个字符,然后检测以该添加的字符为尾部的后缀是否前一半字串等于后一半字串。这样生成的字典序是严格升序排列的。第k小的字典序就可以确定。

Primes = set([2, 3, 5, 7,  11, 13, 17, 19, 23, 29, 31])


def is_prime(x): return x in Primes
vis = [0] * (16 + 2)
C = [0] * (16 + 2)


def prime_ringe(cur, n):
    if cur == n:
        if is_prime(C[0] + C[n - 1]):
            print(C[0:n])
        return
    for i in range(2, n + 1):
        if not vis[i] and (cur == 0 or is_prime(i + C[cur - 1])):
            C[cur], vis[i] = i, 1
            prime_ringe(cur + 1, n)
            vis[i] = 0


def process():
    while 1:
        n = input()
        if n == 0:
            break
        C[0] = 1
        prime_ringe(1, int(n))

UVA140:Bandwidth

给定一个图,对于图中所有点的任何一个排列,定义带宽为:所有点与其图中相邻点在排列中的距离的最大值。求出一个图的最小带宽。

基本思路:直接枚举全排列效率太低,回溯时候可以剪掉一些节点:(1)当某一个节点导致带宽大于最优值时,则不必继续扩展。(2)当某一个节点有m个节点不确定时,那么最理想的情况带宽是m,若m大于最优带宽,也可用剪掉。

min_bandwidth = 100
n = 9
C = [0] * (n)
D = [0] * (n)
vis = [0] * (n + 1)
fina_ans = None


def find_max(graph):
    max_d = 0
    for each in C[1:n]:
        for adj in graph[each]:
            max_d = max(max_d, abs(D[each] - D[adj]))
    return max_d


def Bandwith(graph, cur):
    global n, min_bandwidth, fina_ans
    if cur == n:
        p = find_max(graph)
        if min_bandwidth > p:
            min_bandwidth = p
            fina_ans = C[1:]
        return
    for i in graph:
        ok = True
        if not vis[i]:
            D[i] = cur
            cnt = 0
            for adj in graph[i]:
                if not vis[adj]:
                    cnt += 1
                    if cnt >= min_bandwidth:
                        ok = False
                    continue
                if abs(D[adj] - D[i]) > min_bandwidth:
                    ok = False

            if ok:
                vis[i] = 1
                C[cur] = i
                Bandwith(graph, cur + 1)
                vis[i] = 0
graph = {1: [2, 7, 6], 2: [1, 3, 7], 3: [2, 4], 4: [3, 5, 7],
         5: [4, 8], 6: [1, 7, 8], 7: [2, 4, 6], 8: [5, 6]}
Bandwith(graph, 1)
print(fina_ans, min_bandwidth)

:uva1354

直接暴力枚举,但是枚举的思路是从底层向上开始,每次选择两个节点构造一个新的节点。最后求解宽度则分别算出左边边界和右边边界距离中心的距离,最后相加。这道题讨论了搜索的对象.

def weight(node):
    if type(node)==int:return node
    return node[0]
def width(t):
    def find_bound(t,mark):
        bound = 0
        while type(t)!=int:
            c = weight(t[1])+weight(t[2])
            bound+=weight(t[mark])/c
            t = t[mark]
        return bound
    b1,b2 = find_bound(t,1),find_bound(t,2)
    return b2+b1
def enumerate_tree(vertexs,r):
    left,right = 0,0
    min_w = r*2
    def do(vertexs):
        nonlocal min_w
        if len(vertexs)==1:
            w = width(vertexs[0])
            if w <= r:min_w = min(w,min_w)
            return
        temp = vertexs[::]
        for node1 in vertexs:
            for node2 in vertexs:
                if node1!=node2:
                    temp.remove(node1);temp.remove(node2)
                    temp.append((weight(node1)+weight(node2),node1,node2))
                    do(temp)
                    temp.append(node1);temp.append(node2)
                    temp.remove((weight(node1)+weight(node2),node1,node2))
    do(vertexs)
    print(min_w)
enumerate_tree([1,1,2],3)

Uva 10603:倒水问题。给定三个杯子,容量分别为a,b,c。第三个杯子装满水。没有刻度,要求通过三个杯子之间互相倒水,达到某一个杯子的水量是d。且求出最小的倒水数量。最后,如果不能达到,则求出最接近d的d’。同时求出最小倒水数量。

(1)这道题也是直接利用bfs的思路来进行路径搜索的,但是要求到水量最少,因此我们的就必须要每次拿出倒水量最小的节点来进行扩展,这需要引入优先队列。

(2)其次存储状态判重的时候只需要存储三杯水的状态,至于到水量不能作为状态变量。

def solve(a,b,c,d):
    cap = [0,a,b,c]
    min_state = (0,0,0,c)
    start = (0,0,0,c)
    vis = set((0,0,c))
    water_state = PriorityQueue()
    water_state.put(start)
    while not water_state.empty():
        now = water_state.get()
        if d in now[1:]:print(now);return(now[0],d)
        if abs(d-min_state[3])>abs(d-now[3]):min_state=now
        for i in range(1,4):
            for j in range(1,4):
                if i!=j:
                    if now[i]==0 or now[j]==cap[j]:continue
                    amount = min(now[i],cap[j]-now[j])
                    new = list(now)
                    new[0]+=amount;new[i]-=amount;new[j]+=amount
                    new = tuple(new)
                    if new[1:] not in vis:
                        vis.add(new[1:])
                        water_state.put(new)
    possible_d = min_state[1]
    for x in min_state[1:]:
        possible_d = min(possible_d,x,key = lambda y:abs(y-d))
    return (min_state[0],possible_d)
print(solve(1,12,15,7))

Uva1601

这道题有以下几个难点

(1):移动的物体的数量是n,这不是一个固定值,我采取的办法是利用递归来生成所有的状态。而标准答案的算法则是固定n为3,然后将n小于3的状态置位假节点。

(2):通过预处理将所有的空节点生成一个图,这样遍历的时候就不需要遍历5*5*5次了。

(3):需要避开两个物体交换位置的情况以及占用同一位置的情况。

最后一个小技巧,利用ord(word)-‘a’自动给物体排好序。

def solve(graph,w,h,n):
    direction = {0:(0,0),1:(1,0),2:(0,1),3:(-1,0),4:(0,-1)}
    def construct(graph):
        nodes_table = {}
        for i in range(w):
            for j in range(h):
                p = (i,j)
                nodes_table[p] = []
                if graph[i][j]=='#':continue
                for k in range(5):
                    adj = walk(p,k)
                    if check(adj):
                        nodes_table[p].append(adj)
        return nodes_table
    def walk(p,i):
        x,y = p
        return (x+direction[i][0],y+direction[i][1])
    def create_nodes(q,now,new,cur):
        if cur==n:
            new=tuple(new)
            if check_notswap(now,new) and new not in states:
                states.add(new)
                q.append(new+(now[n]+1,))
            return
        for next in adj_table[now[cur][:n]]:
                new.append(next)
                create_nodes(q,now,new,cur+1)
                new.pop()
    def check(next):
        x,y = next
        if x<w and x>0 and y<h and y>0 and graph[x][y]!='#':return True
        return False
    def check_notswap(now,new):
        for i in range(n):
            for j in range(i+1,n):
                if new[i]==new[j] or (now[i],now[j])==(new[j],new[i]):return False
        return True
    def satisfy(now):
        for i in range(n):
            if now[i]!=capital_pos[i]:return False
        return True
    def search_capital(ans_pos,capital_pos,graph):
        for i in range(w):
            for j in range(h):
                if str.islower(graph[i][j]):ans_pos[ord(graph[i][j])-ord('a')] = (i,j)
                elif str.isupper(graph[i][j]):capital_pos[ord(graph[i][j])-ord('A')] = (i,j)
    states = set()
    ans_pos,capital_pos = [0]*n,[0]*n
    search_capital(ans_pos,capital_pos,graph)
    adj_table = construct(graph)
    q = deque()
    start = tuple([p for p in ans_pos]+[0])
    states.add(start[:n])
    q.append(start)
    while len(q):
        now = q.popleft()
        if satisfy(now):return now
        create_nodes(q,now,[],0)
def process():
    line = input().split(' ')
    h,w,n = [int(x) for x in line]
    graph = []
    for i in range(w):
        line = input()
        graph.append(line)
    print(solve(graph,w,h,n))
process()
版权声明:本文为博主原创文章,未经博主允许不得转载。

算法竞赛入门经典(第二版)-刘汝佳-第七章 暴力求解法 例题(7/15)

说明本文是我对第七章15道例题的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。 另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第七章例题contest ...
  • thudaliangrx
  • thudaliangrx
  • 2016年03月16日 13:17
  • 1611

算法竞赛入门经典(第二版)-刘汝佳-第七章 暴力求解法 习题(2/18)

说明本文是我对第七章18道习题的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。 另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第七章习题contest ...
  • thudaliangrx
  • thudaliangrx
  • 2016年03月16日 13:23
  • 1643

算法竞赛入门经典(第2版)2.5注解与习题

p34 编程记录
  • qq_37589650
  • qq_37589650
  • 2017年04月18日 19:55
  • 161

【算法竞赛入门经典】【第三章】课后习题(第一部分)

《算法竞赛入门经典》【第三章】课后习题详解(第一部分) 包含 习题3-1 分数统计(stat) 习题 3-2 单词的长度(word) 习题3-3 乘积末三位 (product)...
  • luomingjun12315
  • luomingjun12315
  • 2015年04月09日 22:45
  • 1165

算法竞赛入门经典(第2版)习题2-4详解

#include #define MAXN 100 double a[MAXN]={0};int main() { int n,m,count=0; while(1) { ...
  • big_DreamerLzq
  • big_DreamerLzq
  • 2016年06月22日 10:17
  • 1081

(回溯法) 数据结构_回溯法求解迷宫路径

描述:      打印出
  • h302849781
  • h302849781
  • 2014年04月16日 23:14
  • 1008

算法竞赛第七章

例题:Uva1601/埃及分数问题/Uva11212 待续 Uva1601/11212 基本都参考了网上的代码,感觉自己的思想还不够,代码写的还不够…Uva1601 //这题基本是看的网上大...
  • m0_37253730
  • m0_37253730
  • 2017年03月30日 09:44
  • 85

《算法竞赛入门经典》第七章7.1,7.2,7.3(总结)

7.1 一般如果让你求a,b满足h(a,b)=k,k为一个固定的函数,可以通过枚举a,然后用k反向求b,来减少一层循环,两个以上情况同理。 1.技巧总结如果求abcdefgh判断他们是否互补相等,...
  • JACK_JYH
  • JACK_JYH
  • 2017年02月05日 00:07
  • 232

刘汝佳《算法竞赛入门经典(第二版)》习题(二)

刘汝佳《算法竞赛入门经典(第二版)》第二章习题 习题2-1 水仙花数 输出100~999中的所有水仙花数。若3位数ABC满足ABC=A²+B²+C²,则称其为水仙花数。例如:153=1²+5²...
  • qq_37653144
  • qq_37653144
  • 2017年04月22日 22:16
  • 2309

算法竞赛入门经典 习题 2-10 排列(permutation)

习题 2-10          用1,2,3,....,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要求abc:def:ghi=1:2:3。输出所有解。 #include #...
  • oceaniwater
  • oceaniwater
  • 2014年11月02日 14:43
  • 2537
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:算法竞赛入门第七章:回溯与路径寻找
举报原因:
原因补充:

(最多只允许输入30个字)