例,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,若父節點是右孩子,則右孩子連接到父父節點
代碼如下