zoukankan      html  css  js  c++  java
  • 动态规划刷题

    分金子(360公司2017春招真题)

    题目链接

    https://exercise.acmcoder.com/online/online_judge_ques?ques_id=3863&konwledgeId=42
    可以去链接里看一看,不再赘述题目了。

    解题思路

    假设几个变量,f(i,j)是从[i,j]中可以拿到的最大价值,因为只可以从两端拿,假如先手是从最左端拿的,那剩下的最大价值就是f(i+1,j),也就是后手能拿到的最大价值;那先手能拿到的最大价值可以用sum(i,j)-f(i+1,j)来表示。
    假如先手是从最右端拿的,剩下最大价值就是f(i,j-1),也就是后手能拿到的最大价值;那先手能拿到的最大价值可以用sum(i,j)-f(i,j-1)来表示。
    那究竟应该从哪边拿,当然是看f(i+1,j)和f(i,j-1)的大小,最后可以提炼出

    f(i,j) = max{(sum(i,j)-f(i+1,j)),sum(i,j)-f(i,j-1)} = sum(i,j)-min{f(i+1,j),f(i,j-1)}
    

    代码实现

    递归版

    从上式可以看出,递归是一种解决方案,为了避免重复计算,使用一个变量来记录算出来的f(n,m)的值。

    def digui(lst,i,j):
        if(i==j):
            return lst[i]
        elif(res[i][j]!=0):
            return res[i][j]
        else:
            res[i][j]=sum(lst[i:j+1])-min(digui(lst,i+1,j),digui(lst,i,j-1))
            return res[i][j]
    
    n=int(input())
    for q in range(n):
        num=int(input())
        res = [[0]*(num+1) for i in range(num+1)]
        lst=list(map(int,input().split()))
        lst=[0]+lst
        a=digui(lst,1,num)
        b=sum(lst)-digui(lst,1,num)
        print("Case #%s: %s %s"%(q+1,a,b))
    

    动态规划版

    我也说不上来什么叫做动态规划,总之大家都是这样叫的,当初看别人代码时,也是一脸懵比,然后自己整个手推了一遍,豁然开朗。
    我们使用给的案例

    4 7 2 9 5 2
    

    上面是一列金子,先画一张表,为了方便表示,我们从1开始,0列0行都当作是空白

    ‘’ 0 1 2 3 4 5 6
    0 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    1 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    2 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    3 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    4 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    5 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    6 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’

    接下来可以填表了,假如是从[1,1]取,那就一种取法,就是该位置上的数量,其他只有一个金子的情形也是这样,填表。

    ‘’ 0 1 2 3 4 5 6
    0 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    1 ‘’ 4 ‘’ ‘’ ‘’ ‘’ ‘’
    2 ‘’ ‘’ 7 ‘’ ‘’ ‘’ ‘’
    3 ‘’ ‘’ ‘’ 2 ‘’ ‘’ ‘’
    4 ‘’ ‘’ ‘’ ‘’ 9 ‘’ ‘’
    5 ‘’ ‘’ ‘’ ‘’ ‘’ 5 ‘’
    6 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ 2

    那如果是[1,2]该怎么取呢,公式交给我们,应该是先得到[1,2]的和,然后减去较小的那个,那[2,3],[3,4]也是类似。

    ‘’ 0 1 2 3 4 5 6
    0 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    1 ‘’ 4 7 ‘’ ‘’ ‘’ ‘’
    2 ‘’ ‘’ 7 7 ‘’ ‘’ ‘’
    3 ‘’ ‘’ ‘’ 2 9 ‘’ ‘’
    4 ‘’ ‘’ ‘’ ‘’ 9 9 ‘’
    5 ‘’ ‘’ ‘’ ‘’ ‘’ 5 5
    6 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ 2

    规律很明显了,接着填表即可,最后表的状态如下。

    ‘’ 0 1 2 3 4 5 6
    0 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ ‘’
    1 ‘’ 4 7 6 16 11 18
    2 ‘’ ‘’ 7 7 11 16 24
    3 ‘’ ‘’ ‘’ 2 9 7 11
    4 ‘’ ‘’ ‘’ ‘’ 9 9 11
    5 ‘’ ‘’ ‘’ ‘’ ‘’ 5 5
    6 ‘’ ‘’ ‘’ ‘’ ‘’ ‘’ 2

    那[1,6]最大的收益就是1行6列的18了,后面只需要交给计算机来帮我们填表即可。

    n=int(input())
    for q in range(n):
        num=int(input())
        lst = list(map(int,input().split()))
        res = [[0]*(num+1) for i in range(num+1)]
        for i in range(1,num+1):
            res[i][i] = lst[i-1]
        cnt=1
        while cnt<=num:
            i=1
            j=i+cnt
            while j<=num:
                res[i][j] = sum(lst[i-1:j]) - min(res[i][j-1],res[i+1][j])
                i+=1
                j+=1
            cnt+=1
        a = res[1][num]
        b = sum(lst) - a
        print("Case #%s: %s %s"%(q+1,a,b))
    
  • 相关阅读:
    Java入门
    Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8
    pypy3.8安装
    asyncio执行阻塞代码
    linux安装go
    python消费rabbitmq
    绑定进程到指定cpu运行
    负载均衡算法
    django版本规划
    FastAPI WebSocket 简单演示
  • 原文地址:https://www.cnblogs.com/python-dd/p/12564424.html
Copyright © 2011-2022 走看看