連續擼了一整個清明假期的挑戰賽代碼終于結束了。作為比賽的最后,做個小小的總結。
一些小感悟
寫代碼的時候,千萬不要為了方便隨意使用全局變量,使用全局變量后,代碼的耦合度大大提高。
對于這類比賽,能對特定的case進行特定的編程或者調參,對結果會有一定提升(可能會被禁止)。
對于每一次提交,保存好當前的提交版本,使用git是一個不錯的方案。
看不懂的代碼最好不要隨意復制,出了問題怎么改都不知道。
團隊的互相協助,是在比賽中陷入僵局時的解藥。
題目
求解思路
基本思路是最小費用流+遺傳
在剛看到題目的時候,很容易發現這是一個最優化問題,而且屬于NP-難問題。因此我第一個想法就是,采用遺傳或者粒子群算法進行求解,目標函數根據題意很快就可以寫出,我們使用費用函數即可,但是卻不知道怎么個編碼,對服務器編碼 or 網絡流編碼 or both。
直到我找到了最小費用最大流算法,使用該算法,輸入服務器節點,即可計算出從服務器到滿足各個消費節點需求的最小費用網絡流路徑。這樣,結合第一個思路,算法的整體框架就很明確了。我們先通過遺傳算法,產生一堆服務器節點,然后將這些服務器節點輸入到最小費用流算法中,得出各條路徑,通過路徑和服務器信息我們既可以得出該方案下的網絡費用,將費用作為遺傳算法的適應度函數,再使用遺傳算法中的變異、交叉、選擇等操作,選出優秀的染色體,然后返回最小費用流算法,如此迭代循環。下面展示算法的流程圖。

最大流、最小費用算法
算法的具體過程這里我就不展開了,放上比賽時,我們參考的一些文檔與網頁。
SPFA算法
SPFA是一種單源最短路徑算法。在這個題目中,我們使用了最小費用流算法,網絡中存在負權邊,大家熟知的Dijkstra算法便失去了用武之地。SPFA該上場了。
SPFA算法的詳細步驟,看看下面這個鏈接就好了(⊙o⊙)SPFA 算法詳解( 強大圖解,不會都難!)
接下來我說說針對賽題的改進策略。
首先,SPFA算法本身有兩種改進策略:SLF 和 LLL
SLF:Small Label First 策略,設要加入的節點是j
,隊首元素為i
,若dist(j) < dist(i)
,則將j插入隊首,否則插入隊尾。
LLL:Large Label Last 策略,設隊首元素為i
,隊列中所有dist
值的平均值為x
,若dist(i)>x
則將i
插入到隊尾,查找下一元素,直到找到某一i
使得dist(i) <= x
,則將i出對進行松弛操作。
SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高約 50%。
其次,我們在最小費用流使用SPFA算法時,我們只需要知道是否存在從S到T的路徑,而不必需要他們之間的最短路徑,因此,我們在求到了其中的一條路徑時dist != null
,即可返回,不必繼續執行。
程序代碼
比賽的代碼我已經放到GitHub上,寫的比較糟糕,沒有優化,請各位大佬輕噴+_+