華為CodeCraft2016比賽的個人的演算法優化

2021-07-11 03:28:17 字數 2606 閱讀 6338

參加了2016華為codecraft比賽受到刺激很大, 感覺被那些牛人秒成渣渣. 要更努力啊!

我已經"光榮犧牲"了, 所以也就沒什麼心理負擔了.

題目如下:

給定乙個帶權重的有向圖g=(v,e),v為頂點集,e為有向邊集,每一條有向邊均有乙個權重。對於給定的頂點s、t,以及v的子集v',尋找從s到t的不成環有向路徑p,使得p經過v'中所有的頂點(對經過v'中節點的順序不做要求)。

若不存在這樣的有向路徑p,則輸出無解,程式執行時間越短,則視為結果越優;若存在這樣的有向路徑p,則輸出所得到的路徑,路徑的權重越小,則視為結果越優,在輸出路徑權重一樣的前提下,程式執行時間越短,則視為結果越優。

題目跟旅行商問題很相似, 不同點在於一是起點和終點不同; 二是不用經過所有點, 但是必須要經過指定的點集.

首先是路徑用什麼結構來表示, 我的決定是用hashmap, 每個點儲存下乙個點的id. 第乙個原因是這種資料結構可以很輕鬆的在某個點之前或者之後插入乙個點, 大家可能會說linkedlist也行,但是用linkedlist你要首先定位你要查詢的點吧,這就是o(n)的時間複雜度了,而hashmap不需要,知道這個點的id就行;第二個原因是雜湊錶能用o(1)的時間複雜度判斷某個點是否在當前路徑中.

其次是一些簡單的優化,起點的入度可以直接刪除到0,終點的出度直接刪除到0, 其他點如果存在出度或者入度為0的點,遞迴刪除.在這裡還可以進行一些判斷,如果有起點或者v'中的點的出度或者入度為0的話,直接判斷出沒有可行路徑.

下面開始談演算法吧, 我的想法大致經歷了三個過程:

第乙個過程:

最開始完全沒什麼思路,試了試dfs暴力搜尋,然而在v的頂點數大於50之後程式就"不理我了"/(ㄒoㄒ)/~~

第二個過程:

然後在賽事qq群裡聽人討論, 有人用遺傳演算法和蟻群演算法得出結果, 然後自己也試著用遺傳演算法解題. 遺傳演算法的基本步驟如下: 1. 生成一定數量的初始解, 將其記為當前解, 這些解集記為第一代; 2. 對當前代, 通過交叉/變異兩種操作生成一定數量的新解; 3. 在當前代和新解中找出最優的一定數量的解, 其他解的淘汰, 這些解是新的一代; 4. 重複第2和第3兩個步驟, 直到指定代數.

先說說我對遺傳演算法的看法, 我自己對遺傳演算法是很沒底的, 因為它不能**程式的時間複雜度, 其中的人為因素對其影響很大, 所以我如果能用傳統的有確定時間空間複雜度的演算法的話, 就不會去用遺傳演算法. 但是聽說遺傳演算法在最近很火的人工智慧領域作用很大, 所以更加覺得自己菜了-_-",完全看不出遺傳演算法的優勢所在. 

針對我們的問題, 要解決的問題如下:

1.如何在當前解和新解中找出最優解, 評判標準如何界定.

2. 如何生成初始解;

3.交叉/變異的概率如何確定,怎樣進行交叉變異;

對這3個問題, 我的解決方法如下:

1. 評價標準我是這樣考慮的, 對一條路徑來說, 它包含的v'中的點越多越好,權重和越小越好,不存在的路徑越少越好,因此評價是這樣的, 路徑好壞=(v'中的點的個數)/(權重*不存在的路徑條數);

2. 用洗牌演算法對所有的點(除起點和終點)隨機排列生成隨機序列,多生成幾個,這就是初始種群(必然存在很多不存在的路徑,就是兩點間並不相連);

3. 交叉/變異的概率需要多試驗, 取[0, 1]的值. 先說交叉,我的方法是對兩條父代路徑p1, p2, 從p1開始進行遍歷, 如果當前需要交叉,那麼從當前點開始遍歷p2, 就這樣交叉遍歷. 不可避免的會出現已經訪問過的點, 這時候看另外一條父代路徑是否能接著遍歷, 如果可以就從當前點開始遍歷另外一條父代路徑, 否則結束. 再說突變, 這個簡單, 如果當前需要變異, 隨機在所有的點中取個點出來, 如果當前路徑已經存在這個新的點,那麼放棄變異, 否則將當前這個點改變為新的點.

我試著用遺傳演算法解決問題, 可能是我引數不好, 結果比暴力搜尋還不如...

第三個過程:

我的想法是先用dijkstra演算法找出v中每個點到其他可以直接到達(直接到達的意思是不經過v'中的其他點)的v'中的點或者是終點, 然後同樣利用dfs,只不過這次是從起點開始直接找距它最近的v'中的下乙個點, 如果在走到這個最近的v'中的點的路徑中存在已經被訪問過的點,那麼視這個衝突點的前乙個點為新的起點,再找距離它最近的v'中的點,就這樣直到不能走下去為止(當前點的下乙個點就是衝突點或者碰到終點但是v'中還有點沒有經過).

當然了,為了敘述明白省略了一些內容,比如說用dijkstra演算法找最近點是用延遲計算的,就是需要的時候再計算,如果之前已經計算出結果了就不用再計算了.

應該說第三次得到了比較好的結果, 然而依然是被大神秒成渣渣的節奏! 深深感覺到了這個世界的惡意!!

暴力的dfs大概能拿不到20分,

dfs加上dijkstra演算法能達到60多分,

ga我沒勇氣提交...

Code Craft 2016初賽的一點體會

華為codecraft軟體精英挑戰賽初賽於4月11日結束,我們的隊伍有幸進入南京蘇州賽區32強,順利晉級將於5月中旬舉行的複賽。初賽持續了整整乙個月。這乙個月下來,還是很累的。比賽的競爭非常激烈 個人感覺,南京蘇州賽區是全國八大賽區競爭最激烈的 基本上就一直在不停地想新的演算法以及優化 賽題描述起來...

2016華為筆試題

1 有乙個陣列a n 順序存放0 n 1,要求每隔兩個數刪掉乙個數,到末尾時迴圈至開頭繼續進行,求最後乙個被刪掉的數的原始下標位置。以8個數 n 7 為例 0,1,2,3,4,5,6,7 0 1 2 刪除 3 4 5 刪除 6 7 0 刪除 如此迴圈直到最後乙個數被刪除。分析 1 佇列實現 2 網上...

華為2016機試題

輸入一串用空格隔開的數字串,對於數字串的奇數字按公升序排序,偶數字按降序排序.示例輸入 4 6 2 3 6 7 8 1 處理過程 奇數字 4 2 6 8 公升序排序結果 2 4 6 8 偶數字 6 3 7 1 降序排序結果 7 6 3 1 include include using namespac...