由於前幾天開始隨意作了一個JD的題目,因而就打算逐步把JD在賽碼網上的5-3星難度的題目作完,比較有價值的題目會有比較詳細的分析過程,其他的就闡述重點或簡單帶過便可。由於5星難度題目只有兩個,就倒着來作好了。
此次的解析包含了3個4星難度題目,而且從其中一個題目來介紹雙端隊列的概念和使用。算法
<題目來源: 京東2017秋招 原題連接-可在線提交(賽碼網)>數組
戰爭遊戲的相當重要環節就要到來了,此次的結果將決定王國的生死存亡,小B負責首都的防衛工做。首都處於一個四面環山的盆地中,周圍的n個小山構成一個環,做爲預警措施,小B計劃在每一個小山上設置一個觀察哨,日夜不停的瞭望周圍發生的狀況。
一旦發生外敵入侵事件,山頂上的崗哨將點燃烽煙。若兩個崗哨所在的山峯之間沒有更高的山峯遮擋且二者之間有相連通路,則崗哨能夠觀察到另外一個山峯上的烽煙是否點燃。因爲小山處於環上,任意兩個小山之間存在兩個不一樣的鏈接通路。知足上述不遮擋的條件下,一座山峯上崗哨點燃的烽煙至少能夠經過一條通路被另外一端觀察到。對於任意相鄰的崗哨,一端的崗哨必定能夠發現一端點燃的烽煙。
小B設計的這種保衛方案的一個重要特性是可以觀測到對方烽煙的崗哨對的數量,她但願你可以幫她解決這個問題。網絡
先準確理解題意:在一個環上有若個點,每一個點都和它相鄰的兩個點連通,選定其中任何兩個點a和b,若是a從順時針或者逆時針方向到b所通過的點沒有比a和b中較小的點更高時(注意若是3個點的高度相等時候,是能夠相互觀察的),a和b能夠相互觀察。最終咱們須要知道有多個這樣的(a, b)對。數據結構
實際上,最簡單粗暴的辦法就是3一個三重循環進行掃描,首先2重循序去枚舉一個觀察對(a, b),再用一重循環檢查(a, b)中有沒更高的山就能夠了。
好消息是這個題目數據很弱,這個O(n^3)的算法徹底能夠經過測試,賽碼網上的大多數人也是用的這個方法。
壞消息是這並非一個很好方法,若是按題目給定的數據規模是沒法經過的。app
接下來咱們來觀察這樣一個O(n^3)的算法的執行過程,並從中找到一些能夠優化的地方。首先,在不考慮環而且咱們選定順時針方向的狀況下,咱們計算(a, b)是否能夠相互觀察,咱們會掃描mount[a + 1], mount[a + 2], ... mount[b - 1],在掃描的過程當中若是發現第一個mount[i] > mount[a],那麼此後任何點都不能被a觀察到。學習
a.若是咱們在掃描的過程當中,發現mount[a + 1] > mount[a],那麼a就不能觀察到後續的任何點,也就沒有必要對後面的點再進行掃描後了。接下來,若是mount[a + 2] > mount[a + 1],這個過程是一樣的,mount[a + 1]一樣沒法觀察到以後的任何一個點,一樣不須要掃描。若是整個mount[i]是一個單調遞增的序列,顯然除了相鄰的兩個點能夠觀察,其他任何兩個點是沒法觀察的(這裏不暫考慮環的狀況)。
b.若是mount[a + 1] < mount[a],那麼mount[a]就有可能觀察到a + 1後面的點,所以咱們須要再看接下來的狀況,這個時候假設mount[a + 2] > mount[a + 1],顯然是mount[a]能夠觀察到mount[a + 2],而mount[a + 1]不能再觀察到後續的任何點(規則a說明),假設mount[a + 2] < mount[a + 1],那麼mount[a + 2]也可能觀察到後面的點,須要繼續看接下來狀況。測試
綜合上面2點規則,咱們須要維護一個不遞增(注意,是不遞增,而不是遞減,這就說明了有相等的狀況)這樣一個單調性的線性結構,當向這個線結構中加入一個mount到尾部時,咱們就從尾部一個mount一個mount的和當前加入的這個進行比較,如尾部的這個mount < 當前加入的,就須要將這個尾部mount移除,繼續這個比較,直到線性結構中的某個mount >= 當前加入的,或者整個線性結構已經移除了全部的mount。還應當注意到,新加入mount和出隊的全部mount都是能夠相互觀察,此外,相鄰的兩個mount能夠相互觀察。優化
如今咱們須要一個支撐咱們實現上述操做的數據結構--雙端隊列(deque),雙端隊列是一個能夠在隊列的任何一端均可以進行入隊和出隊操做的一個數據結構。對這個在題目而言,咱們在隊首進行出隊操做(一會在解決環的問題的時候會用到),在對尾進行入隊和出隊操做。spa
接下來,須要解決的就是環的問題。解決環的方式通常能夠採用將原來的環切段,而且從切斷位置再複製一份這個斷掉的環(也就是一根鏈了),還有就是取模的形式。均可以。對於這個問題,咱們只須要從當前位置考慮到後面n個位置便可。例如,如今n=5 共有5個mount,在處理mount[3]的時候,咱們須要向後考察mount[4],mount[5],而後是mount[1],最後是mount[2]。也就是,雙端隊列中只須要保留最多n個的mount,第n+1個加入的時候,須要將第1個從隊首出隊。再來觀察個例子,如今n=5,且這個5個mount的高度都是不單調不增加的,當n=5進入雙端隊列時,隊列中有5個元素,當第6個mount入隊時(其實是第一個),顯然就產生了重複,1不須要再觀察到本身,更不須要繞過本身一圈再觀察後面的mount,因此,此時應該將1從隊首中出隊。設計
至於題目任何兩個mount能夠從兩個通路觀察(順時針或者逆時針),其實已經不須要處理,由於咱們都從任何1個點向一個方向觀察了n個長度,從mount[a]若是能夠觀察mount[b],也就是從mount[b]能夠觀察mount[a],也就是咱們在處理a時考察了a->b是否能夠觀察,在處理b時,考察了b->a是否能夠觀察。因爲可能會有重複的狀況,所以咱們須要設置一個set類型(而不是obv[][]這樣一個二維數組來標記,要知道題目的數據規模是n=10^6)來保存從任何一點能觀察的其餘的點,這樣的存儲空間也比較理想。
至此,問題尚未解決。還有一個相等的問題須要處理,也就是一樣高的mount,由於題目是要求兩個mount以前沒有更高的mount就能夠觀察。由於3個或者更多相等的mount中,只要沒有更高的,也是能夠相互觀察的。這部分咱們在雙端隊列中處理時,若是遇到相等,要繼續向前找到不相等的。可是不要將這些mount出隊。
至於算法複雜度,前面我簡單到提到過,儘管下面的代碼也使用了3層的循環,但要注意實際的執行次數,任何一個元素在雙端隊列中最多被入隊一次,出隊一次(不考慮重複的狀況)。
import sys const_val = 0 const_num = 1 result = 0 def add_pair(a, b, pair): global result if a > b: a, b = b, a if b not in pair[a]: pair[a].add(b) # print a + 1, b + 1 result += 1 def main(): global result while True: result = 0 h = [] deque = [] line = map(int, sys.stdin.readline().strip().split()) if len(line) < 1: return n = line[0] line = map(int, sys.stdin.readline().strip().split()) for elem in line: h.append(elem) pair = [set() for i in range(n)] for i in range(2 * n): seq = i if i < n else i - n if len(deque) and deque[0][const_num] == seq: deque.pop(0) while len(deque) > 0: t = deque[-1] add_pair(t[const_num], seq, pair) if deque[-1][const_val] < h[seq]: deque.pop(-1) else: for q in deque[::-1]: add_pair(q[const_num], seq, pair) if q[const_val] != h[seq]: break break deque.append((h[seq], seq)) print result if __name__ == '__main__': main()
<題目來源: 京東2016實習生 原題連接-可在線提交(賽碼網)>
臨近期末,讓小東頭疼的考試又即將到來了,並且是小東最不喜歡的科目。遺憾的是,小東得知d天后她必須參加這次考試。小東的父親對她要求很是嚴格,要求她當即開始複習功課。爲照顧她的情緒,父親要求她天天該科目的學習時間在iminTime到imaxTime之間,並計劃在考前檢查小東是否按要求作了。若未能完成,小東將會受到懲罰。
如今小東的父親要求檢查小東的備考狀況。遺憾的是,因爲專一於備考,小東只是記錄了本身備考的總時間sumTime,並無記錄天天覆習所用的時間,也不知道準備狀況是否符合父親的要求。她想知道是否可以製做一個知足要求的時間表以應付父親的檢查。
小東但願你可以幫到她,你是否願意?
雖然都是4星題目,剩下兩個題目相對就很簡單了。而且剩下兩個頭目比較相似,咱們做爲一類問題處理吧。
這個題目由於天天最有個最大和最小的限制。咱們先來考慮可行性,也就是隻須要考察兩個極端狀況是否知足總的學習時間。
∑MinTimeDay[i] <= sumTime <= ∑MaxTimeDay[i] i∈[1..n]
知足後,根據題目要求儘量在前面的天數多學習,那麼咱們從頭開始處理,首先保證知足天天的最低時間,剩餘的sumTime - ∑MinTimeDay[i]儘量的從一開始就多分配,但不要超過MaxTimeDay[i],用完後,其他的就按MinTimeDay[i]時間學習便可。
import sys def main(): day = [] while True: line = map(int, sys.stdin.readline().strip().split()) if len(line) < 2: return d, sum_time = line[0], line[1] t_min = 0 t_max = 0 for i in range(d): line = map(int, sys.stdin.readline().strip().split()) day.append((line[0], line[1])) t_min += line[0] t_max += line[1] if t_min <= sum_time <= t_max: sum_time -= t_min print 'Yes' for d in day: if d[1] - d[0] <= sum_time: print d[1], sum_time -= d[1] - d[0] elif 0 < sum_time < d[1] - d[0]: print d[0] + sum_time, sum_time = 0 else: print d[0], print else: print 'No' day[:] = [] if __name__ == '__main__': main()
<題目來源: 京東2017秋招 原題連接-可在線提交(賽碼網)>
小B曾經酷愛網絡遊戲,整日通宵達旦的玩遊戲,致使身體素質急劇降低,所以下決心痛改前非,遠離一切電子產品,並經過遠足登山的方式改變生活方式並提升身體素質。因爲擔憂對身體形成太大的負荷,他老是選擇最平坦的路徑,並記錄天天的行程狀況及達到的最高海拔,使得連續兩天之間的海拔之差最多爲一個單位。不幸的是,在行程結束時,他不當心掉進河裏,形成部分記錄信息遺失。他想知道本身行程中可能達到的最高海拔,你是否可以幫忙?
解決這個題目咱們須要按d做爲key進行一個排序,按時間整理登山的高度後,考察兩個橫向的距離,也就是間隔的天數。所以題目規定天天只能上下最多一個單位高度,由於間隔的天數決定了咱們在縱向能夠移動的高度。
首先檢查第i個記錄和i+1個記錄的合理性。
abs(reci + 1 - reci) <= reci + 1 - reci
計算最大可達到的高度
h = (reci + 1 - reci + reci + 1 + reci) / 2
最後注意兩個端點的處理便可。
import sys const_d = 0 const_h = 1 def main(): while True: rec = [] line = map(int, sys.stdin.readline().strip().split()) if len(line) < 2: return n, m = line[0], line[1] for i in range(m): line = map(int, sys.stdin.readline().strip().split()) rec.append((line[0], line[1])) rec = sorted(rec) flag = True result = 0 for i in range(m - 1): if abs(rec[i + 1][const_h] - rec[i][const_h]) > rec[i + 1][const_d] - rec[i][const_d]: flag = False print 'IMPOSSIBLE' break else: h = (rec[i + 1][const_d] - rec[i][const_d] + rec[i + 1][const_h] + rec[i][const_h]) / 2 # print '---->', h hy_max = max(rec[i + 1][const_h], rec[i][const_h]) result = max(result, max(hy_max, h)) if flag: hy_board_max = max(rec[0][const_h] + rec[0][const_d] - 1, rec[-1][const_h] + n - rec[-1][const_d]) result = max(hy_board_max, result) print result if __name__ == '__main__': main()