一、題目描述(網易)算法
有 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的狀況下,最大最小值都爲56
2:在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)
二、題目描述(騰訊):
騰訊大廈有39層,你手裏有兩顆一抹一眼的玻璃珠。當你拿着玻璃珠在某一層往下扔的時候,必定會有兩個結果,玻璃珠碎了或者沒碎。大廈有個臨界樓層。低於它的樓層,往下扔玻璃珠,玻璃珠不會碎,等於或高於它的樓層,扔下玻璃珠,玻璃珠必定會碎。玻璃珠碎了就不能再扔。如今讓你設計一種方式,使得在該方式下,最壞的狀況扔的次數比其餘任何方式最壞的次數都少。也就是設計一種最有效的方式。app
試題分析:
本題要考慮最壞狀況,同時也要保證成功,就像若是一開始選擇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次,這裏從最高處開始考慮,3九、39-一、39-2-一、39-3-2-1......spa
三、題目描述(網易).net
小易來到了一條石板路前,每塊石板上從1挨着編號爲:一、二、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。
輸入3d
4 24
輸出code
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]
四、 題目描述(滴滴出行)
給定一個有n個正整數的數組A和一個整數sum,求選擇數組A中部分數字和爲sum的方案數。
當兩種選取方案有一個數字的下標不同,咱們就認爲是不一樣的組成方案。輸入描述:
輸入爲兩行:
第一行爲兩個正整數n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行爲n個正整數A[i](32位整數),以空格隔開。輸出描述:
輸出所求的方案數
試題分析:
本題利用遞歸複雜度太大,利用動態規劃比較好,以下面的示例,sum爲15的方案爲{二、三、五、5}{二、三、10}{五、10}{五、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]
五、題目描述(美團點評)
有一個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]
六、題目描述(美團點評)
給你六種面額一、五、十、20、50、100元的紙幣,假設每種幣值的數量都足夠多,編寫程序求組成N員(N爲0-10000的非負整數)的不一樣組合的個數
輸入描述:
輸入爲一個數字N,即須要拼湊的面額
輸出描述:
輸出也是一個數字,爲組成N的組合個數。
示例:
輸入
5
輸出
2
試題分析:本題最簡單作法就是for循環,可是太耗時,利用動態規劃是最好的選擇,見下圖:(大佬詳解本題)
這裏舉兩個例子講解一下基本思想:
對於20(在最大面值能使用10的狀況下):首先最大面值爲5的狀況下,有5種狀況(這個是上一步這裏不考慮);在最大面值爲10的狀況下,也就是10+10:10、10,10 、五、5;10、五、1*5;10、1*10,等同於10的4種狀況10,五、5,五、1*5,1*10,因此總共5+4=9種。
對於25(在最大面值能使用20的狀況下):首先最大面值爲10的狀況下,有12種狀況;在最大面值爲20的狀況下,也就是20+5:20、5,20、1*5,不考慮20,等同於5的兩種狀況5、1*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]