动态规划算法题(5题)

北城以北 提交于 2020-03-24 23:53:20

1、题目描述(网易)

   有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

输入描述:

   每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:

输出一行表示最大的乘积。
试题分析:本题要使用动态规划来解,动态规划的特点:1.求解的是最优化问题;2.可以分解为最优子结构本题可以先求解在第i个学生的位置下,j(j<K)个学生的能力值的最大值,得到所有学生位置下j个学生的能力值的最大值;在j个学生的情况下,得到j+1个学生的最大值,最后得到k个学生的最大值,下面以一个例子来解释(注意因为有负数,所以要计算最小值,同时保存):样例输出:     10     8 7 2 -7 9 5 4 10 -7 1     3 3输出:   630如上,第一步先计算k=2的情况:7:在d=3的情况下,最大最小值都为562:在d=3的情况下,最大值为16,最小值为14-7:在d=3的情况下,最大值为-14,最小值为-56......得到第一趟的结果k=3的情况下(这里以第一趟的结果为基础,只有这样就不需要考虑第一趟中d=3的限制):2:在d=3的情况下,最大最小值都为112(56*2)-7:在d=3的情况下,最大值为-98(14*-7)最小值为-392(56*-7)9:在d=3的情况下,最大值为504(56*9)最小值为-504(-56*9)......得到第二趟的结果返回最大值就是最后的结果
#-*- coding:utf-8 -*-

n=input()
array=[int(i) for i in raw_input().split()]
k,d=[int(i) for i in raw_input().split()]
# n=36
array_max=array_min=array
#轮询k-1趟即可
for i in range(0,k-1):
    _max=[-float('inf')]*n#将最大值的数组赋值无穷小
    _min=[float('inf')]*n#将最小值的数组赋值无穷大
    for j in range(i+1,n):
        if j<=d+i:#下面对应的min、max都是考虑到array[j]为负值的情况下
            temp_max = max(max(ii*array[j] for ii in array_max[i:j]),max(ii*array[j] for ii in array_min[i:j]))
            temp_min = min(min(ii*array[j] for ii in array_max[i:j]),min(ii*array[j] for ii in array_min[i:j]))
        else:
            temp_max = max(max(ii*array[j] for ii in array_max[j-d:j]),max(ii*array[j] for ii in array_min[j-d:j]))
            temp_min = min(min(ii*array[j] for ii in array_max[j-d:j]),min(ii*array[j] for ii in array_min[j-d:j]))
        _max[j]=temp_max
        _min[j]=temp_min
    array_max=_max
    array_min=_min
    print array_max
    print array_min
print max(array_max)

 2、题目描述(腾讯):

腾讯大厦有39层,你手里有两颗一抹一眼的玻璃珠。当你拿着玻璃珠在某一层往下扔的时候,一定会有两个结果,玻璃珠碎了或者没碎。大厦有个临界楼层。低于它的楼层,往下扔玻璃珠,玻璃珠不会碎,等于或高于它的楼层,扔下玻璃珠,玻璃珠一定会碎。玻璃珠碎了就不能再扔。现在让你设计一种方式,使得在该方式下,最坏的情况扔的次数比其他任何方式最坏的次数都少。也就是设计一种最有效的方式。

试题分析:本题要考虑最坏情况,同时也要保证成功,就像如果一开始选择20层,结果只有两个,碎和没碎两个结果,如果碎了,那么就必须从1楼开始,这样才能成功,最坏也就是20次(正好也就是20层),也就是考虑最坏情况下。对于本题:先选择一个楼层x,扔一个球,:  球碎,那么再从一楼开始扔球,直到球碎求到结果,即最坏会扔x次  球没碎,再选楼层y扔球:    球碎,那么从x+1层开始,直到球碎,即最坏会扔:1(x层那一次)+y-x+1(y层那一次)次    球没碎,再选楼层z扔球:      球碎:那么从y+1层开始,直到球碎,即最坏会扔:1(x层那一次)+1(y层那一次)+z-y+1(z层那一次)次      ......解题思路1:
  

  第一次选择楼层x,第二次选择x+x-1层,第三次选择x+x-1+x-2......可以得到x=9

  解题思路2:

  因为是最坏情况,所以最好的情况为3次,最坏情况还是9次,这里从最高处开始考虑,39、39-1、39-2-1、39-3-2-1......

3、题目描述(网易)

小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3.......
这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达。
例如:
N = 4,M = 24:
4->6->8->12->18->24
于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板

 

试题分析:初始化一个数组[0~m],置为-∞,先求n下能到达的位置,将对应的step[i]置为1,接着从n+1开始,若step[i+1]为-∞,跳过,否者将能到达的step[i]置为(step[n+1]+1,step[i])min,直至得到m为止,跳出循环,或者循环结束,返回-1。
  示例:

  输入

  4 24

  输出

  5


#-*- coding:utf-8 -*-
import math
#求对应数的因子数
def get_list(i,m):
    list = [] #能到达的石阶
    num = int(math.sqrt(i)) + 1
    for k in range(2, num):
        if i % k == 0:
            t=i/k  #对应的因子
            if i+t<=m:
                list.append(i+t)
            if i+k<=m:
                list.append(i+k)
    list.sort()
    return list

if __name__=='__main__':
    n,m=[int(i) for i in raw_input().split(' ')]
    l=[float('inf') for i in range(0,m+1)]
    l[n]=0
    for i in range(n,m+1):
        if l[i]==float('inf'):continue
        list=get_list(i,m)
        for j in list:
            l[j]=min(l[j],l[i]+1)
        print l
        if l[m]!=float('inf'):
            break
    if l[m]==float('inf'):
        print -1
    else:
        print l[m]

4、 题目描述(滴滴出行)

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。

输入描述:

输入为两行: 第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000) 第二行为n个正整数A[i](32位整数),以空格隔开。

输出描述:

输出所求的方案数

试题分析:

本题利用递归复杂度太大,利用动态规划比较好,如下面的示例,sum为15的方案为{2、3、5、5}{2、3、10}{5、10}{5、10},利用动态规划求每一个数被组成的可行方案,如下图:

示例:

  输入

  5 15 5 5 10 2 3

  输出

  4
#-*- coding:utf-8 -*-

if __name__=='__main__':
    n,s=[int(i) for i in raw_input().split(' ')]
    #不需要添加大于S的数
    l=[int(i) for i in raw_input().split(' ') if int(i)<=s]
    #添加长度为s+1的数组,即0~s
    result=[0 for i in range(0,s+1)]
    #序号0置为1
    result[0]=1
    for i in range(0,len(l)):
        temp=result[:]
        for j in range(0,s+1):
            if result[j]>0:
                #防止数组越界
                if j+l[i]<=s:
                    temp[j+l[i]]+=result[j]
        result=temp[:]
    print result[s]

5、题目描述(美团点评)

  有一个X*Y的网格,小团要在此网格上从左上角到右下角,只能走格点且只能向右或向下走。请设计一个算法,计算小团有多少种走法。给定两个正整数int x,int y,请返回小团的走法数目。

输入描述:

  输入包括一行,逗号隔开的两个正整数x和y,取值范围[1,10]。

输出描述:

  输出包括一行,为走法的数目

示例:输入    3 2  输出    10试题分析:该题是一个很典型的动态规划问题,但第一想到的可能是用深度优先遍历去实现,也就是递归,用递归思维很简单,从起点开始从右、下分别出发,直到结束点;用动态规划的思想是,到点[x,y](这里将网格点看成二维数组)的走法是到点[x-1,y]和点[x,y+1]的和,从[x-1,y]往下走就到了[x,y],[x,y-1]往右走就到了[x,y]。递归实现代码如下:
# -*- coding:utf-8 -*-

def fun(x,y):
    global n,m,num
    if x==n or y==m:
        num+=1
        return
    elif x<n and y<m:
        fun(x+1,y)
        fun(x,y+1)

if __name__ == '__main__':
    n,m=map(int,raw_input().split(' '))
    num=0
    fun(0,0)
    print num
动态规划实现代码如下:
# -*- coding:utf-8 -*-

if __name__ == '__main__':
    x, y = map(int, raw_input().strip().split())

    d = [[0 for j in range(y + 1)] for i in range(x + 1)]
    for j in range(y + 1):
        d[0][j] = 1
    for i in range(x + 1):
        for j in range(y + 1):
            d[i][j] = d[i - 1][j] + d[i][j - 1]
    print d[x][y]

6、题目描述(美团点评)

  给你六种面额1、5、10、20、50、100元的纸币,假设每种币值的数量都足够多,编写程序求组成N员(N为0-10000的非负整数)的不同组合的个数

输入描述:

  输入为一个数字N,即需要拼凑的面额

输出描述:

  输出也是一个数字,为组成N的组合个数

示例:

  输入

    5

  输出

    2

试题分析:本题最简单做法就是for循环,但是太耗时,利用动态规划是最好的选择,见下图:(大佬详解本题

这里举两个例子讲解一下基本思想:

  对于20(在最大面值能使用10的情况下):首先最大面值为5的情况下,有5种情况(这个是上一步这里不考虑);在最大面值为10的情况下,也就是10+10:101010 、5、510、5、1*510、1*10,等同于10的4种情况105、55、1*51*10,所以总共5+4=9种。

  对于25(在最大面值能使用20的情况下):首先最大面值为10的情况下,有12种情况;在最大面值为20的情况下,也就是20+5:20、520、1*5,不考虑20,等同于5的两种情况51*5 所以总共12+2=14种。

代码实现如下:

# -*- coding:utf-8 -*-

if __name__ == '__main__':
    n=input()
    print [i for i in range(0,n+1)]
    l=[1 for i in range(n+1)]
    for i in [5,10,20,50,100]:
        temp=l[:]
        for j in range(i,n+1):
            if j%5!=0:
                temp[j]=temp[j-1]
            else:
                temp[j]=l[j]+temp[j-i]
        print temp
        l=temp
    print l[n]

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!