關於圖算法的總結

 例,01問題

求長度爲n的01串,滿足如下條件

1,長爲L0的連續子串中0的個數不少A0,不多於B0

2,長爲L1的連續子串中的1的個數不少於A1,不多於B1

如果不存在,輸出-1

 

這個題可以構造圖,然後求圖的各點的最短路徑。

由於上述1,2條件的存在,對於任意的k,用f(k)表示前k串的和

則f(k+L0)-f(k)>=L0-b0

   f(k+L0)-f(k)<=L0-a0

 

   f(k+L1)-f(k)>=a1

   f(k+L1)-f(k)<=b1

 

   f(k+1)-f(k)>=0

   f(k+1)-f(k)<=1

 

根據<=的關係可以構造一個圖,圖中的各個節點就是從1到n,關係如上不等式。相當於求n個點到點0的最短距離。而相鄰的兩個點與0點之間的距離就是我們要求的該位置的0或者1

 

由於該算法中權值存在負值,所以用bellman鬆弛法求單源最短路徑

源代碼如下

 

bellman_ford算法

BELLMAN_FORD(G,w,s)

 Init-single-source(G,s)

for i<-1 to |V[G]|-1

do for each edge(u,v) in E[G]

do relax(u,v,w)

for each edge(u,v) in E[G]

do if d[v]>d[u]+w(u,v)

then return FALSE

return TRUE

 

鬆弛法

Relax(u,v,w)

if d[v]>d[u]+w(u,v)

then d[v]<-d[u]+w(u,v)

trace[v]<-u;

 

 

 

同時複習一下Dijkstra算法

 

Dijkstra(G,w,s)

 Init-single-source(G,s)

S=null;

Q=V[G];

while Q!=null

 do u=min(Q)

 S=S+{u}

 for each vertex v in Adj[u]

do Relax (u,v,w)

附一個Dijkstra算法的實現

 

for(int i=1;i<=pointNum;i++)

{

min=MaxInt;

for(int j=i+1;j<=pointNum;j++)

{

if(!isVisited[j])

{

if(d[1][j]<min)

{

minPoint=j;

min=d[1][j];

}

}

}

isVisited=true;

for(int j=1;j<=pointNum;j++)

{

if(d[pointNum][j]+min<d[1][j])

d[1][j]=d[pointNum][j]+min;

}

}

 

 

2,二分圖

求二分圖的最大匹配有兩種算法:最大流,匈牙利算法

求二分圖的最佳匹配:最大流。

 

最大流的算法基於以下的思想:殘留網絡,增廣路徑,最小割。

求最大流的算法過程:1,隨意找一個源點到終點的增廣路徑。2,求該網絡的殘留網絡。3,在殘留網絡上找增廣路徑,將增廣路徑上可通過的最大值加到該路徑上的圖的流上。4重複2,3直到沒有新的增廣路徑。找增廣路徑的時候用廣度優先算法可以增加算法的效率。

 

對二分圖求最大最優匹配的時候,只用額外的加一個源點和終點即可。

求最大匹配最好還是用匈牙利算法。

 

以下用匈牙利算法來求解,國王心愛的王子選妻子的題目:國王有n個兒子,宮裏有n個美女。每個王子喜歡一些姑娘(艹),國王對每個兒子的喜歡程度不同爲Ai,如果王子挑選到了喜歡的姑娘,國王的高興程度+Ai^2。找一個算法,讓國王足夠滿意。

 

關於二分圖的另一個說明:完全二分圖的邊的數目小於n^2/4,證明很簡單,省略。

在下面的程序中,王子爲1~n,女孩爲n+1~2n,邊的數目正好爲n^2,所以開了n^2條邊。

 

  

 

關於二分圖題目2

求二分圖中的必須的邊

 

算法有兩種:1,枚舉所有的邊,然後用匈牙利算法求最大匹配,如果找不到最大匹配,則是必須的

       這種算法的情況下,如果是鄰接矩陣的話,枚舉每條邊需要的時間是O(n^2),當然也可以存邊,存邊的話O(e),匈牙利算法的複雜度爲O(EN)

 

  2,縮小查找空間,必須的邊一定在一個完備匹配中,因爲必須的邊在所有的完備匹配中,如果少了這個邊,一個完備匹配都找不到,所以可以先找一個完備匹配,然後剔除其中的邊,用匈牙利算法驗證。這樣的話,將邊的範圍減小了。

 

代碼如下

 

 

 

3, 線段樹的應用

問題:樹的統計。一棵含有n個節點的樹,所有節點的編號依次爲1,2,3,...,n。對於編號爲v的節點,定義t(v)爲後代中所有編號小於v的節點個數。請計算每個節點i的t(i)。

用到的數據結構,樹,線段樹。算法dfs

不妨先思考這樣一個方法:假如這是一顆二叉樹。我們將這棵二叉樹保存在數組裏面。從數組最末開始,向前掃描(本質是從葉子節點開始)。對於每一個節點,和父節點n/2比較,如果比這個父節點小,父節點的t值+1,然後父節點的父節點。算法複雜度爲(nlogn)當然二叉樹比較好的情況的時候。不好的時候複雜度就要高,最壞n^2。

對於這棵樹,不方便用二叉樹來做。

 

本題的算法很有意思。它是這樣做的,先對樹做一次正向dfs先序遍歷,得到遍歷序列,然後反向dfs先序,得到一個遍歷序列,而,兩個遍歷序列重疊的部分,正好是樹根的子節點。那麼

t=正向dfs中小於v的節點數量+逆向中的+祖先中小於v的-整棵樹小於v的(整棵樹小於v的爲v-1個)

 

該算法對線段樹進行了靈活的應用,值得學習。一下是代碼和測試用例

 

 

 

測試用例

 

15

0

0

2 12 15

0

0

2 5 8

5 1 3 4 9 10

0

2 6 11

1 14

0

0

0

2 2 13

0

 

 

運行結果0 0 0 0 0 1 6 0 3 1 0 0 0 2 0

 

4,多源點最短路徑問題

 

考慮對遠點進行合併,然後使用dijkstra算法,記住壓縮圖和原圖之間點序號的映射

 

 

 

5, 閉包運算的題目

 

如何計算閉包,實際上就是floyd算法的思想。

兩種方法

1,採用寬度優先或者深度優先遍歷來解決。從任意一個頂點出發,進行一次遍歷,就可以求出次頂點和其他各個頂點的聯通狀況。所以只需要把每個頂點做爲出發點遍歷一次,就能夠知道任意兩個頂點之間是否有路存在。窮舉每個頂點,總的時間複雜度是O(n*n)

2,類似floyd算法,如下算法實現的那樣。

 

題目:運動員之間可以傳遞信息,但不是每兩個運動員之間都可以相互傳遞信息。求教練至少要通知幾個人纔可以使每個運動員都知道自己的命令。

算法,先計算閉包,給每個閉包賦予一個序號。

然後按照序號將圖壓縮,一個傳遞閉包一個節點。

統計入度爲0的節點的個數,即爲教練需要通知的次數

 


floyd算法又稱floyd-warshall算法,是一種動態規劃算法,用來求一個有向圖G中每對頂點間的最短路徑問題,其運行時間爲O(v^3)

 

定義n*n的矩陣W。過程範圍最短路徑的權值矩陣爲D(n)

 

Floyd-Warshall(W)

n<-rows(W)

D(0) <-W

     for(k=1~n)

         do for i=1~n

             do for k=1~n

 

             do d(k)ij =min(d(k-1)ij , d(k-1)ik +d(k-1)kj )

return D(n)

 

6, 使用floyd算法的例子。

染色

一個N個點的無向完全圖,每條邊都被染成[1,m]的一種顏色,要求選擇一部分顏色,作爲一個集合S,且S中的元素個數不超過(m+1)/2,並滿足如下性質:

圖中任意兩個點a和b,一定存在一條長度不大於3的通路,其每條邊的顏色都屬於S。

簡單的分析:

將m種顏色一分爲2,然後證明必有一半的顏色滿足。

 

使用然後,設A集合邊爲1,B集合的邊的權重爲4。使用floyd算法,求兩點之間的最短距離。如果超過了3,則結果爲B集合,否則爲A集合。

代碼如下

 

 

 7,二叉樹的應用

 

坐船問題:某學校有n個學生去公園划船。一條船最多可以坐兩個人。如果某兩個學生同姓或者同名就可以坐同一條船。學校希望每個學生都上船,但是小船的租用費用很高。學校想要租最少的船。請問學校要租多少條船。

 

用二叉樹來解決。左孩子同姓,右孩子同名。在選擇將誰放在一條船上的時候有以下幾種情況

1,如果父節點已經被其他節點使用了,則增加一條船。

2,如果父節點沒有被使用,並且父節點的另一個節點已經被使用,則當前節點和父節點一條船

3,如果非1,2情況。若父節點是父父節點的左孩子,則父節點和右孩子被訪問,左孩子連接到父父節點

4,若父節點是右孩子,則右孩子連接到父父節點

 

代碼如下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章