算法竞赛入门第七章:竞赛选讲(Uva12325,Uva1603)

来源:互联网 发布:centos命令大全 编辑:程序博客网 时间:2024/05/18 02:11

Uva12325:

这道题很有思维强度,首先比较容易想到的是枚举宝箱的数量,从0到[n/s1]或者0到[n/s2]。但是由于输入很大,当s1,s2都很小的时候,那么枚举的数量将会非常的巨大。因此针对s1,s2较小而n很大的情况,需要提供另一种思路。

另一种枚举的思路是从s1,s2入手的。因为s1,s2很小,如果枚举的数量和他们成正相关,那么就可以确保枚举的效率。考虑一个事实:s1个宝物1和s2个宝物2是同样的体积,那么对于任意的宝物s1个宝物1和任意的s2个宝物2,我们总可以相互替换。只需要比较s2*v1与s1*v2的大小,就可以确定某一个宝物至多只能取s1-1或s2-1种,否则就可以相互替换。这样枚举的数量就限制在min(s1-1,s2-1,[N/s1],[N/s2])中了。

def solve(n,s1,v1,s2,v2):    max_v = 0    def recur_search(s1,v1,s2,v2):        nonlocal max_v        for k1 in range(0,n//s1+1):            remain = n-k1*s1            k2 = remain//s2            max_v = max(max_v,k1*v1+k2*v2)    def search_2(s,s1,v1,s2,v2):        nonlocal max_v        for k1 in range(0,s+1):            remain = n-k1*s1            k2 = remain//s2            max_v = max(max_v,k1*v1+k2*v2)    if n//s1<10000:recur_search(s1,v1,s2,v2)    elif n//s2<10000:recur_search(s2,v2,s1,v1)    elif s2*v1<s1*v2:search_2(s2-1,s1,v1,s2,v2)    else:search_2(s1-1,s2,v2,s1,v1)    print(max_v)

Uva1374使用IDA*算法效率会高一点。如果使用BFS效率将会非常低。对于队列,可能取元素和放元素比for循环要慢很多,其次,对于由于BFS很难进行剪枝,因为很多元素会重复计算,最后,判重的时候注意需要对状态进行排序,否则会造成状态的重复。但是排序,添加就需要很多的时间。

另外IDA*的一个好处是不需要在最后一次迭代中展开整个解答树。

(1)尽量先拿两个较大的数先加再减,这样可以尽快接近目标。

(1)总是使用最新得到的元素进行操作。

def solve(n):    def dfs(d,state):        nonlocal ok,max_d        if d==max_d:            if n in state:ok = True;            return        u = max(state)        if u<<(max_d-d)<n:return        for op in [1,-1]:            for j in range(d,-1,-1):                next = state[d]+op*state[j]                if next<=0 or next in state:continue                if u>n and next>n:continue                state[d+1] = next                dfs(d+1,state)                if ok:return    max_d = 0    ok = False    for max_d in range(1,20):        ok = False        dfs(0,[1]+[0]*(max_d))        print(max_d)        if ok:print(max_d,);break

BFS

def solve_3(n):    def bfs():        state = deque()        vsit = set()        vsit.add((1,))        state.append((1,))        while len(state[-1]):            now  = state.popleft()            if n in now:return len(now)            for i in range(len(now)-1,-1,-1):                for op in [1,-1]:                    next=tuple(sorted(now+(now[-1]+now[i]*op,)))                    if next not in vsit and next[-1]>0 and next[-1] not in now:                        vsit.add(next)                        state.append(next)    print(bfs()-1)

Uva1603这道题有一定难度,关键是要想到如何将正方形和边联系到一起。注意,我们以正方形作为搜索对象,然后将边存在正方形里面。我们将每一个可能的正方形都进行编号然后存储它们的边数,以及相关联的边(即时该正方形已经被去掉边了)。这样我们从小正方形开始,因为小正方形被破坏之后大的正方形也可能会被破坏掉,这样就减少许多节点。

(1)如果我们将每一个正方形都动态的维护起来,那么编程上可能会比较繁琐,特别是在搜索时要维护正方形的状态。因此就干脆只维护固定的正方形,然后再判断该正方形是不是完全正方形。

(2)可以进行剪枝,注意这样一个事实,如果当前的边里面最多的一个关联到x个正方形,那么后面的最理想的情况是每一次都可以减少x,则(maxd-d)*x

def solve(n,missing):    def row_match(x,y):        return (2*n+1)*x+y    def col_match(x,y):        return (2*n+1)*x+y+n    def init():        for v in missing:exists[v-1] = 0    remains,maxsize = 0,60    exists,matches = [1]*2*n*(n+1),[[] for i in range(2*n*(n+1))]    size,fullsize = [0]*maxsize,[0]*maxsize    contains = {}    s = 0    init()    for L in range(1,n+1):        for x in range(0,n-L+1):#x from 0,n-L            for y in range(0,n-L+1):#y from 0,n-L                contains[s] = set()                fullsize[s] = 4*L                for j in range(0,L):#j from 0,L-1                    up = row_match(x,y+j)                    down = row_match(x+L,y+j)                    left = col_match(x+j,y)                    right = col_match(x+j,y+L)                    e = [up,down,left,right]                    for t in e:                        contains[s].add(t)                        matches[t].append(s)                    size[s] += exists[up]+exists[down]+exists[left]+exists[right]                if size[s]==fullsize[s]:remains+=1                s+=1    def find_square():        for i in range(s):            if size[i]==fullsize[i]:return i        return -1    def process_edge():        for i,x in enumerate(matches):            num = 0            for squ in x:                if size[squ]==fullsize[squ]:num+=1            matches[i] = num    def dfs(d,remains):        nonlocal maxd,s,ok        if d==maxd:            if find_square()==-1:ok = True            return        most_destroyed = max(matches)        if (maxd-d)*most_destroyed < remains:return        k = find_square()        for edge in contains[k]:            for j in range(s):                if edge in contains[j]:                    if size[j]==fullsize[j]:                        remains -= 1                        matches[edge]-=1#square j is complete                    size[j] -= 1            dfs(d+1,remains)            if ok:return            for j in range(s):                if edge in contains[j]:                    if size[j]==fullsize[j]:                        remains +=1                        matches[edge]+=1#the square j is complete                     size[j] += 1    ok = False    process_edge()    for maxd in range(1,n*n):        ok = False        dfs(0,remains)        print(maxd)        if ok:print(maxd);break

dfs法,相对来说效率会慢一些。因为第一次的搜索可能会搜到一个比较深的解,那么后来的优化剪枝就相对比较低效。

def solve(n,missing):    def row_match(x,y):        return (2*n+1)*x+y    def col_match(x,y):        return (2*n+1)*x+y+n    def init():        for v in missing:exists[v-1] = 0    remains,maxsize = 0,60    exists,matches = [1]*2*n*(n+1),[[] for i in range(2*n*(n+1))]    size,fullsize = [0]*maxsize,[0]*maxsize    contains = {}    s = 0    init()    for L in range(1,n+1):        for x in range(0,n-L+1):#x from 0,n-L            for y in range(0,n-L+1):#y from 0,n-L                contains[s] = set()                fullsize[s] = 4*L                for j in range(0,L):#j from 0,L-1                    up = row_match(x,y+j)                    down = row_match(x+L,y+j)                    left = col_match(x+j,y)                    right = col_match(x+j,y+L)                    e = [up,down,left,right]                    for t in e:                        contains[s].add(t)                        matches[t].append(s)                    size[s] += exists[up]+exists[down]+exists[left]+exists[right]                if size[s]==fullsize[s]:remains+=1                s+=1    def find_square():        for i in range(s):            if size[i]==fullsize[i]:return i        return -1    def process_edge():        for i,x in enumerate(matches):            num = 0            for squ in x:                if size[squ]==fullsize[squ]:num+=1            matches[i] = num    def dfs_1(d,remains):        nonlocal best        if d>= best:return        most_destroyed = max(matches)        if (best-d)*most_destroyed < remains:return        k = find_square()        if k==-1:best=d;print(best);return        for edge in contains[k]:            for j in range(s):                if edge in contains[j]:                    if size[j]==fullsize[j]:                        remains -= 1                        matches[edge]-=1#square j is complete                    size[j] -= 1            dfs_1(d+1,remains)            for j in range(s):                if edge in contains[j]:                    size[j] += 1                    if size[j]==fullsize[j]:                        remains +=1                        matches[edge]+=1#the square j is complete     process_edge()    print(matches)    ok = False    best = n*n    dfs_1(0,remains)    print(best)
0 0