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

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)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值