最近比較忙,又有一段時間沒寫題目了,終於在前幾天把賽碼網上,JD的2016秋招和2017實習生招聘剩下的4星難度題目作了,至此全部4星難度題目都解決了,5星難度題目還剩下一個應該是計算幾何學的題目,由於這塊我不熟悉,後面找時間再處理。
鑑於賽碼網的難度分類不是特別準確,接下里我把1-3星難度的題目過一次,若是有價值的就和你們一塊兒分享。
此次的解析包含了4個題目,相對比較簡單,但須要考慮全面,不然容易掉坑。java
<題目來源: 京東2016實習生招聘 原題連接-可在線提交(賽碼網)>python
某糖果公司專門生產兒童糖果,它最受兒童歡迎的糖果有A一、A2兩個序列,均採用盒式包裝。包裝好的A1類糖果體積爲一個存儲單位,而包裝好的A2類糖果體積正好是A1類的兩倍。算法
這兩類糖果之因此廣受兒童歡迎,是由於糖果中含有公司獨家研發的魔幻因子。A1或A2序列中的糖果,看起來包裝多是同樣的,但由於其中的魔幻因子含量不一樣被細分爲不一樣的產品。app
臨近傳統節日,公司的糖果供不該求。做爲一個精明的糖果分銷商,小東但願可以藉此大賺一筆,因而帶着現金開着貨車來公司提貨。貨車的容量是肯定的,小東但願採購的糖果可以儘量裝滿貨車,且糖果的魔幻因子總含量最高。只要不超出貨車容量,糖果總能夠裝入貨車中。函數
小東但願你能幫她解決這一問題。性能
這個題目的描述彷佛不是太準確,其實題目就是說有n個糖果,每一個糖果有個體積(1或者2),此外,有個魔幻因子。最後咱們須要解決是在體積不超過v的狀況下,選擇若干糖果而且儘量的得到最大的魔幻因子總量。學習
這看起來是個01揹包問題*[1],觀察數據規模後發現,n <= 10^5,v <= 10^9這個用於狀態轉移的枚舉量實在太大,並且單就一個v的上限就足以撐爆空間。看來問題彷佛不是直接使用01揹包的動態規劃能夠解決的,再觀察題目條件,一個比較特別的地方是糖果的體積只有1和2的兩種狀況,咱們從這個比較特殊的條件來解決問題。優化
a.考慮一種最簡單的狀況:只有體積爲1的糖果。
顯然咱們只須要對全部的糖果按照魔幻因子從大到小排序,從魔幻因子最大的糖果開始選擇,因爲體積都是1,那麼給定的v必定能夠達到。這些選擇出來的糖果的魔幻因子的和就是咱們的求解目標。spa
b.再考慮另一種罪簡單的狀況,只有體積爲2的糖果。
一樣咱們須要按魔幻因子的大小進行排序,而後從魔幻因子最大的糖果開始選擇,可是不一樣於a,因爲是每一個糖果的體積是2,那麼選出來糖果的體積和不必定能夠恰好爲v,若是恰好爲v則問題已經解決。若是超過v,顯然是整體積比v大了1,那麼最後選擇的這一種糖果就不要了。這時咱們選擇的糖果的整體積爲v-1,儘管浪費了1,可是咱們沒有任何辦法再放入糖果了。.net
c.考慮題目既有體積1和2的狀況。
基於上述兩種基本狀況的分析,咱們仍然須要安排魔幻因子大小進行排序,儘量選擇魔幻因子高的優先選擇,再考慮臨界狀況的處理。須要注意到,對於體積爲2的糖果,咱們不該該直接按照其魔幻因子的總量進行排序,而是按照單位體積的魔幻因子含量進行排序。這是由於,儘管可能體積爲2的糖果魔幻因子可能很高,可是他佔用2個體積,若是兩個體積爲1的糖果魔幻因子沒有其高,可是兩個加起來就超過了,而且最終佔用的體積都是2.
排序以後咱們按照從大的開始選擇,若是能夠恰好取到v,那麼問題就獲得解決。若是得不到,那麼顯然當前取的是體積2的糖果,而且是超過1。這個時候,明顯咱們能夠有多種選擇,咱們既能夠在已經選擇的糖果中去掉一個體積爲1的且魔幻因子最小的糖果,也能夠是在尚未選擇的糖果中選擇一個體積爲1的且魔幻因子最大的一個糖果。至於具體選擇哪一種就看哪一種方案能夠得到更高的魔幻因子總和了。
那麼問題彷佛已經解決了,而後咱們並無考慮到選擇的和沒選擇的中均可能不存在體積爲1的糖果。
...已選擇部分...| 體積爲2的糖果| ...未選擇部分...
^已得到的魔幻因子pm ^體積限制v
設已選擇的最小的體積爲1的糖果的魔幻因子爲l
設未選擇的最小的體積爲1的糖果的魔幻因子爲r
當前體積2的糖果魔幻因子爲p,體積不超過v得到最大的魔幻因子總和爲pm
i.l和r都不存在
iii.l不存在,r存在
最後一個須要解決的問題是輸出選擇糖果的序列問題,這並不難,咱們排序後依次記錄選擇了的糖果便可。pm - l + p的狀況,刪除記錄中的l便可。可是,因爲存在多種方案要得失編號儘量的小,那麼對與ii, iv中存在相等的狀況時的處理原則時,選擇編號較小的便可。
排序時間複雜度爲O(nlogn),因爲每一個糖果只須要處理一次,計算部分時間複雜度爲O(n),很是理想。
import sys const_n = 0 const_v = 1 const_p = 2 def main(): while True: line = map(int, sys.stdin.readline().strip().split()) n, vm = line[0], line[1] candies = [] for i in range(n): line = map(int, sys.stdin.readline().strip().split()) if len(line) < 2: break candies.append((i + 1, line[0], line[1])) candies = sorted(candies, key=lambda s: float(s[const_p]) / float(s[const_v]), reverse=True) # print candies seq = set([]) v = p = 0 l = r = -1 for i, candy in enumerate(candies): if v + candy[const_v] >= vm: if v + candy[const_v] == vm: seq.add(candy[const_n]) p += candy[const_p] else: for j in range(i + 1, n): if candies[j][const_v] == 1: r = j break if l == -1 and r == -1: pass elif l == -1 and r != -1: p += candies[r][const_p] seq.append(candies[r][const_n]) elif l != -1 and r == -1: if candy[const_p] - candies[l][const_p] > 0: p = p - candies[l][const_p] + candy[const_p] seq.remove(candies[l][const_n]) seq.add(candy[const_n]) else: if candy[const_p] - candies[l][const_p] > candies[r][const_p]: p = p - candies[l][const_p] + candy[const_p] seq.remove(candies[l][const_n]) seq.add(candy[const_n]) else: p += candies[r][const_p] seq.add(candies[r][const_n]) break seq.add(candy[const_n]) v += candy[const_v] p += candy[const_p] if candy[const_v] == 1: l = i print p if p > 0: seq = sorted(seq) for s in seq: print s, else: print 'No' print if __name__ == '__main__': main()
<題目來源: 京東2017實習生招聘 原題連接-可在線提交(賽碼網)>
收到情報,有批新造的機器人要運輸到前線。小C將去破壞機器人的運輸。小C將激光炮放置在公路的一旁,等運輸車通過的時候發射(假設激光炮必定能夠射穿車輛)。因爲能源有限,激光炮只能發射兩次。能夠認爲激光炮放在座標軸的原點處,並向y軸正方向發射。每輛運輸車能夠看做是一個矩形,起始的x軸座標爲Xi ,全部的車均位於第一象限,長度爲Li,速度爲1,朝x軸負方向運動。即通過t時間後,該車車頭的x座標爲Xi-t,車尾座標爲Xi-t+Li 。只要打中車的任何一個部分就算擊中。
請你算算,他在哪兩個時刻發射,才能摧毀最多的運輸車。
先簡化下題目的意思,因爲每一個車輛的前進速度是統一的,那麼實際上咱們就不用考慮車子移動的狀況了,只須要在x軸上選擇兩個點發射激光炮便可。此外,這個題目的數據規模是X、L<= 10^9 而不是109。
須要注意,第2次發射的激光炮不會擊毀被第一次發射的激光炮已經擊毀的車子。也就是1輛車不能被擊毀兩次。咱們就不能單純在按某個點上的車輛最多和次多來選擇x軸上發射激光炮的位置了。考慮到只能發射兩枚激光炮,那麼咱們只須要枚舉這2個位置就能夠了。
可是觀察X和L 10^9的限時,直接枚舉x軸上的兩個點顯然不能夠。
咱們考慮兩個車有重疊的狀況,要判斷有沒有辦法一炮機會這兩個車子,實際上咱們只須要判斷兩個位置便可,即某個車頭的點是在另一個車的範圍內便可。這是一種對於極限狀況的考慮,若是該狀況成立,就還會有若干其餘的點可能知足,但這部分點就不須要再判斷。換句話說,兩個車若是有重疊,那麼其中有個車的車頭必定在另一個車的車身範圍內。
擴展到通常狀況,根據上面的討論,對於全部的車,咱們激光炮的發射位置只須要選擇每一個車車頭的位置便可。枚舉以後再判斷哪些車子被擊毀,作上標記。最後記錄一個最大的擊毀數目便可。因爲車輛總數n僅僅200,先兩重循環枚舉2個車頭位置,再用一層循環檢查擊毀數目,算法的時間複雜度爲O(n^3)是能夠接受的。
import sys def calc(f, s, c, n): destroy = [False for i in range(n)] res = 0 for i, elem in enumerate(c): if elem[0] <= f <= elem[1] or elem[0] <= s <= elem[1]: if not destroy[i]: res += 1 destroy[i] = True return res def main(): n = map(int, sys.stdin.readline().strip().split())[0] cars = [] segments = [] for i in range(0, n): line = map(int, sys.stdin.readline().strip().split()) cars.append((line[0], line[0] + line[1])) segments.append(line[0]) r = 0 for i in range(len(segments)): for j in range(i): r = max(r, calc(segments[i], segments[j], cars, n)) print r if __name__ == '__main__': main()
<題目來源: 京東2017秋招 原題連接-可在線提交(賽碼網)>
小明同窗學習了不一樣的進制以後,拿起了一些數字作起了遊戲。小明同窗知道,在平常生活中咱們最經常使用的是十進制數,而在計算機中,二進制數也很經常使用。如今對於一個數字x,小明同窗定義出了兩個函數f(x)和g(x)。
f(x)表示把x這個數用十進制寫出後各個數位上的數字之和。如f(123)=1+2+3=6。
g(x)表示把x這個數用二進制寫出後各個數位上的數字之和。如123的二進制表示爲1111011,那麼g(123)=1+1+1+1+0+1+1=6。
小明同窗發現對於一些正整數x知足f(x)=g(x),他把這種數字稱爲幸運數,如今他想知道,小於等於n的幸運數有多少個。
接下來的這兩個題目相對比較簡單,數據範圍也不大,這個題目能夠直接按照題目意思模擬便可。
注意如下兩點問題:
1.這類題目都要採用離線的計算方式,一次性先把數據規模內的數據所有計算到出來並保存,而後根據輸出直接輸出結果便可。
2.相對來講這個題目的數據規模較弱,注意到在當次計算時,若是個位沒有發生進位的話,結果就是上次計算的結果+1,若是發生了進位再所有計算一次便可。這樣能夠節約很是大的一個計算量。
提交後我大體看了下運行時間,我使用的python耗時369ms,而其餘python和java耗時接近或者超過1000ms,其中一個C++的運行時間爲623ms。有時候一個很小的優化帶來的性能提高是很顯著的。
import sys const_max_n = 100000 + 1 def _next(bits, last_sum, base): next_add = 0 bits[0] += 1 last_sum += 1 if bits[0] >= base: last_sum = 0 for i in range(len(bits)): bits[i] += next_add if bits[i] >= base: bits[i] = 0 next_add = 1 else: next_add = 0 last_sum += bits[i] if next_add == 1: bits.append(1) last_sum += 1 return last_sum def main(): r = [0 for i in range(0, const_max_n)] _dec = [0] _bin = [0] last_bin_sum = last_dec_sum = 0 for i in range(1, const_max_n): last_bin_sum = _next(_dec, last_bin_sum, 2) last_dec_sum = _next(_bin, last_dec_sum, 10) r[i] = r[i - 1] if last_bin_sum == last_dec_sum: r[i] += 1 t_case = map(int, sys.stdin.readline().strip().split())[0] for i in range(0, t_case): n = map(int, sys.stdin.readline().strip().split())[0] print r[n] if __name__ == '__main__': main()
<題目來源: 京東2016實習生招聘 原題連接-可在線提交(賽碼網)>
三子棋是一種你們熟知的遊戲,幾乎全部人都會玩。遊戲規則至關簡單,兩人依次在一個3X3棋盤格上下棋,一我的畫叉,另外一我的畫圈。任何一我的畫的三個記號若是造成構成一條水平、垂直或對角的直線則獲勝,遊戲結束。畫叉的人先開始遊戲,若是全部的棋盤格都畫滿了但兩人都不能獲勝,則遊戲平局結束。
遊戲在一個3X3的棋盤上進行,每一個棋盤格單元處於空白、畫叉或畫圈狀態中的一種,你的任務是肯定下一輪由誰下棋:
1:輪到先手下棋;
2:輪到後手下棋;或者是斷定遊戲的狀態:
x:給定的棋局不是合法的棋局;
1 won:先手獲勝;
2 won:後手獲勝;
Draw:平局;小東對棋類遊戲頗有研究,這一次三子棋比賽中,她被邀請做爲評判,爲了提攜後進,她請你幫忙斷定。
本題看似簡單,其實要注意的地方還比較多,包括如何比較優雅的完成代碼的編寫(這我代碼也寫不太好)。
注意到本題的條件具備前後順序。應該是下列順序:
1.給定的棋局是否合法;
2.是否有獲勝方;
3.平局
4.該誰下棋
這類題目建議使用一個方向向量,方向向量能夠指示要判斷的方向。好比,要判斷一個從上到下的對角線狀況,咱們能夠設置一個(1,1)的方向向量,當從(0,0)開始後:
sx = 0, sy = 0
for i <- 0 to 2:
sx += d[0]
sy += d[1]
便可,對於本題,設置一個判斷8個方向的起始位置後,再對應設置8個方向向量便可。這樣對於咱們編寫代碼十分有利。能夠最大限度的減小代碼量,便於閱讀。
import sys
start = [(2, 0), (1, 0), (0, 0), (0, 0), (0, 0), (0, 1), (0, 2), (2, 0)]
vector = [(0, 1), (0, 1), (0, 1), (1, 1), (1, 0), (1, 0), (1, 0), (-1, 1)]
def main():
while True: x = o = 0 board = [] for i in range(3): line = map(str, sys.stdin.readline().strip().split()) if len(line[0]) < 3: return for l in line[0]: if l == 'X': x += 1 elif l == '0': o += 1 board.append(list(line[0])) legal = True w1 = w2 = False if 0 <= x - o <= 1: for i in range(8): d = {'0': 0, 'X': 0, '.': 0} dx, dy = start[i][0], start[i][1] for j in range(3): t = d.get(board[dx][dy]) d[board[dx][dy]] = t + 1 dx += vector[i][0] dy += vector[i][1] if d.get('X') == 3: w1 = True if d.get('0') == 3: w2 = True if w1 and w2 or x > o and w2 or x == o and w1: legal = False else: legal = False if legal: if w1: print '1 won' elif w2: print '2 won' else: if x + o == 9: print 'draw' elif x == o: print '1' else: print '2' else: print 'x'
if name == '__main__':
main()
*[1] 關於01揹包問題及其擴展能夠參考下列文章:
http://blog.csdn.net/mu399/ar...
http://blog.csdn.net/insistgo...