圖論難點:問題的轉化和抽象(可看成特殊的某一類DP)
圖論與DP的聯系:
DP問題(從集合角度分析最優化問題)可以看成從F(0,0)、F(0,1)、F(1,2)......F(0,m)到F(n,m)的最長路。因此DP問題可以轉化為拓撲圖(一般DP問題的狀態間無環)上的最短(長)路。
當DP依賴關系不具有拓撲序時(即存在環時),可以將其轉化為最短路,也可以用高斯消元。
//TLE的原因常常是沒有memsert
//st數組在不同算法中的用法:
單源最短路的建圖方式
AcWing 1129. 熱浪
//單源最短路模型。朴素或堆優化Dijkstra、spfa皆可
AcWing 1128. 信使
//數據范圍較小,Floyd算法直接處理
//核心:對於每個點來說,它接收到信的時間,等於它到指揮部 的最短距離。
//跑完最短路之后找出所有最短路中的最大值,若為正無窮則說 明該點與起點不連通,輸出“-1”即可
//注意預處理時d[i][i]=0。
AcWing 1127. 香甜的黃油
//多源匯最短路問題(可用堆優化Dijkstra或Spfa)
//分析數據范圍得知,Floyd算法會超時.所以可以枚舉每一個點作為起點時的花費
AcWing 1126. 最小花費
//注意扣除手續費的計算:
w[i]=1-手續費i (0<w[i]<1)
100=d[A]*w[1]*w[2]*w[3]*w[4]......
所以為使d[A]最小,應使w[1]*w[2]*w[3]*w[4]......最大。
//令S=w[1]*w[2]*w[3]*w[4]......:
logS=log(w[1]*w[2]*w[3]*w[4]......)
=logw[1]+logw[2]+logw[3]+logw[4]......
因為0<w[i]<1,所以logw[i]<0,logS<0,-logS>0。那么,為使S最大,logS就應最大,-logS就應最小。其中,-logS可以看成是-logw[i]的和。
所以本題就可以轉化成一個最短路問題,故可以用Dijkstra或spfa求解。代碼可以直接用乘法來實現三角不等式,上述只是為了說明本題運用Dijkstra或spfa的正確性。
AcWing 920. 最優乘車
//sstream讀入小技巧(原題未告知一行讀入多少數):
//注意這道題的特殊性:要求的是換乘次數最少的路線,而非最短路。可以將問題轉化,在處於同一路線上的站點之間連一條權值為1的邊,然后用經典最短路模型處理。因為所有的邊權都是1,所以可以直接寫BFS。
AcWing 903. 昂貴的聘禮
//tap:建立虛擬源點(標為0號點),表示直接買的價格,此時應注意一共有n+1個點。
//本題需要考慮等級限制:因為等級和等級限制都不超過100,所以可以枚舉等級區間,在這個區間內進行Dijkstra,時間復雜度不超過100w。
單源最短路的綜合應用
AcWing 1135. 新年好
//最短路+DFS 6*O(m)+5!
- 先預處理出從1,a,b,c,d,e出發到其他所有點的單源最短路徑
- DFS所有擺放順序,5!,對於每種擺放順序,可以通過查表的方式算出最短距離。
//當一個數組作為函數的形參被傳入時,sizeof函數就無法正常工作,會RE。
AcWing 340. 通信線路
//最短路+二分
//定義在[0,1000001]這個區間中的性質如下:
對於區間中的某一個點,求出從1走到N,最少經過的長度大於x的邊的數量是否小於等於k。
//求出從1到N最少經過幾條長度大於x的邊:
可以將所有的邊分類:如果邊長大於x,則邊權看成1,否則邊權是0。可以用雙端隊列BFS來求從1到N的最短路。
AcWing 342. 道路與航線
//這道題的路徑有兩種,一種是邊權非負的雙向邊(道路),一種是邊權可正可負且無環的單向邊(航線)。
//本題spfa會被卡。
- 如果邊權非負,那么可以Dijkstra算法,時間復雜度是mlogn
- 如果是拓撲圖,那么不管邊權是正是負,均可按拓撲序掃描,時間復雜度是線性的
//航線一定在不同的道路連通塊之間,將每一個連通塊看成一個點。具體實現方式:
1.先輸入所有雙向道路,然后DFS出所有連通塊,計算兩個數組:id[]存儲每個點屬於哪個連通塊;vector<int> block[]存儲每個連通塊里有哪些點
2.輸出所有航線,同時統計出每個聯通塊的入度
3.按照拓撲序依次處理每個連通塊。先將所有入讀為0的連通塊的編號加入隊列中
4.每次從隊頭取出一個連通塊的編號bid
5.將該block[bid]中所有點加入堆中,然后對堆中所有點跑dijkstra算法
6.每次取出堆中距離最小的點ver
7.然后遍歷ver的所有鄰點j。如果id[ver]==id[j],那么如果j能被更新,則將j插入堆中;如果id[ver]!=id[j],則將id[j]這個連通塊的入度減一,如果減成0了,則將其插入拓撲排序的隊列中。
//體現單源最短路:
memset(dist,0x3f,sizeof dist);
dist[S]=0;
AcWing 341. 最優貿易
//最短路算法和DP的結合(本質上DP和最短路算法是有很大交集的,絕大部分DP都可以被看成是拓撲圖上的最短路或最長路問題.)。采用類似於DP的思想,把所有從1到n的路徑看成一個集合,進行子集的划分。
//假設S1,S2…St都可以走到第K個點.那么:。dmax同理。
//DP問題所說的“無后效性”即轉化為一個表示狀態的圖時,圖上是不存在環的,但是這個問題中存在。因此可以用最短路算法處理上面的類DP問題。
//用spfa正確性:
for(迭代n-1次)
for(循環邊)用三角不等式更新
Dijkstra算法中,每個點只出隊一次,但是在本題中一個點可以被更新多次,所以Dijkstra算法不適用於這道題。
//從1到k買入的最小值dmin[k]
從k到n賣出的最大值dmax[k]
答案即max(dmax[k]-dmin[k])
單源最短路的擴展應用
AcWing 1137. 選擇最佳線路
//可以建反向邊,求出從終點到每個起點的最短路的最小值
原問題:從每個起點出發,到達終點的所有路徑的最小值。
加上虛擬源點之后的問題:從虛擬源點出發,到達終點的所有路線的距離的最小值。將原問題轉化成了單源最短路問題。
AcWing 1131. 拯救大兵瑞恩
//應用拆點的思想。最短路中需要拆點,分層的題目.一般都可以用DP的方式先考慮。因為有鑰匙等因素的影響,因此f(i,j)並不能只用兩維就表示出到點(i,j)的最短距離。仿照DP的思路,我們可以再加一維。
//但是這道題並不能按照DP的方式來寫,因為一般能夠用DP方式寫的題目都需要保證在計算某個狀態之前,所有能夠更新它的狀態都已經被更新了,即其狀態與狀態之間滿足拓撲序。但是這道題某些點之間是可以相互到達的,因此存在環。所以還是要用最短路的方式來寫。
//將二維變成一維:f(x,y,state)的前兩維可以壓縮成一維
//動態規划
一、狀態表示:d(x,y,state)
1.集合:所有從起點走到(x,y)這個格子,且當前已經擁有的鑰匙是state的所有路線的集合。
2.屬性:最短距離
二、狀態計算
1.(x,y)這里有一些鑰匙,那么可以直接將所有鑰匙拿起:
2.向上下左右四個方向走。
(1)沒有門和牆
(2)有門,且有匹配的鑰匙,之后走到了(a,b):
//狀態間存在環形依賴,因此可以將DP轉化成圖論問題(BFS)
//Taps:
1.用一維數字表示二維位置
2.所有邊權都是0或1,因此可以用雙端隊列,保證時間復雜度是線性的。
AcWing 1134. 最短路計數
//求方案數
- 先求出全局最小值是多少
- 分別求出每個子集中等於全局最小值的元素個數
//最短路樹(拓撲圖)上保證不存在邊權為0的環,否則答案的方案數則為正無窮。
//本題用BFS或Dijkstra做沒有問題,但是不能用spfa
//求方案數時如果有負權邊,則先用spfa求一遍最短路,再循環遍歷每一條邊,若該邊滿足三角不等式,則加入最短路樹中,最后將最短路樹建好,再由拓撲序求最短路樹上的方案數。
AcWing 383. 觀光
//最短路徑的條數
次短路徑:如果存在比最短路徑長度恰好多1的路徑,則再把這樣的路徑條數加上
d[i,0]表示從1到i的最短路徑 cnt[i,0]
d[i,1]表示從1到i的次短路徑 cnt[i,1]
Floyd算法(兼具DP和圖論的性質)
1.多源多匯最短路:可在的時間復雜度之內求出任意兩點間的最短路
2.傳遞閉包:將所有可以間接到達的點連上直接到達的邊
如果存在i->j這條邊,則g[i,j]=1
如果不存在i->j這條邊,則
(1) 初始化:d[i,j]=g[i,j]
(2) for(k)
for(i)
for(j)
if(d[i,k]&&d[k,j])
d[i,j]=1;
3.找最小環
4.恰好經過k條邊的最短路(倍增思想)
//Floyd和Bellman-Ford、spfa算法都是基於DP
Dijkstra基於貪心
//動態規划
一、狀態表示
d[k,i,j]表示從i到j只經過1~k的話,最短路徑是多少
1.集合:所有從i出發,最終走到j,且中間只經過節點編號不超過k的所有路徑。
2.屬性:路徑長度的最小值
二、狀態計算
1.所有不含節點k的路徑
最小值為d[k-1,i,j]
2.所有包含節點k的路徑
最小值為d[k-1,i,k]+d[k-1,k,j]
去掉第一維后:
下面來證明循環當中是否可以去掉第一維:
特殊情況:當k=j時,
由於經過預處理,d(k,k)=0,所以d(i,k)=d(i,k)。說明在循環一輪后,d(k,i,k)仍然等於d(k-1,i,k)。同理得d(k,k,j)=d(k-1,k,j)。所以在循環中可以去掉第一維。
AcWing 1125. 牛的旅行
//牧區:節點
牧場:連通塊
//具體思路:
(1) 用floyd算法求出任意兩點之間的最短距離
(2) 求maxd[i],表示和i連通的且距離i最遠的點的距離
(3) 情況1:答案為所有maxd[i]的最大值
情況2:枚舉在哪兩個點之間連邊,需要滿足d[i,j]=INF。答案為
//本題數據較大,INF最好定義為1e20
AcWing 343. 排序
//假定如果A<B,則d(A,B)=1
1.矛盾:d(i,i)=1
2.唯一確定:當i≠j時,d(i,j)、d(j,i)中必有一個是1
3.順序不唯一
//改良 將變成
:
AcWing 344. 觀光之旅
//集合:按照環上編號最大的點分類
如果d(i,j)=d(i,k)+d(k,j),則k為i->j中編號最大的點
AcWing 345. 牛站
//d[k,i,j]表示從i到j,恰好經過k條邊的最短路徑
(本題中可以存在負環)
//滿足結合律,那么就可以用倍增(快速冪)的思想處理這道。由d(2,i,j)得到d(4,i,j),再得到d(8,i,j)
因此時間復雜度可優化為
//因為實際用到的點數遠小於總的點數,所以可以把點的編號離散化一遍,降低時間復雜度。
最小生成樹(無向邊)
當前與外界直接相連的權值最小的一條邊。這條邊一定可以出現在最優解中。
如何證明當前這條邊一定可以被選?
假設不選當前邊,最終得到了一棵樹。然后將這條邊加上,那么必然會出現一個環,在這個環上,一定可以找出一條長度不小於當前邊的邊,那么把當前邊替換上去,結果一定不會變差。
AcWing 1140. 最短網絡
AcWing 1141. 局域網
//相當於在這個圖的每個連通塊內,求一棵最小生成樹。相當於求原圖的“最小生成森林”。
//做Kruskal算法:
- 將所有邊權從小到大排序
- 依次枚舉每條邊a,b,w
如果a和b不連通,那么就將當前邊加到最小生成樹中去。
//可以用Prim算法時一定可以用Kruskal算法,但用Kruskal算法時不一定可以用Prim算法(例如本題)
AcWing 1142. 繁忙的都市
//普通的最小生成樹:所有的邊權之和最小
本題中的最小生成樹:最大的邊權最小
//做法:Kruskal
- 將所有邊從小到大排序
- 從小到大依次枚舉每條邊,a,b,w
如果a和b已經連通,那么直接pass
如果a和b不連通,那么就將當前邊選出來
AcWing 1143. 聯絡員
//讀題->分析模型->算法->代碼
1.將所有必選邊加到並查集中
2.將所有非必選邊從小到大排序
3.從小到大依次枚舉每一條非必選邊,a,b,w
如果a和b已經連通,直接pass
如果a和b不連通,那么就將當前邊選上
//Kruskal算法:可以只實現前一半,也可以在已經有邊的前提下繼續做后一半
AcWing 1144. 連接格點
//注意!這不是一個最小生成樹問題:n個點,m條邊,邊權可正可負,求將所有點連通的最小邊權和是多少?
//O(klogk)->O(k)
//將二維變成一維
//容易沖突的變量名:y1,next,prev,hash
//為了省去排序的時間,先將縱向邊(權值為1)加入,再將橫向邊(權值為2)加入
最小生成樹的擴展應用
定理:
任意一棵最小生成樹一定可以包含無向圖中權值最小的邊
證明:
反證法。假設無向圖G=(V,E)存在一棵最小生成樹不包含權值最小的邊。設e=(x,y,z)是無向圖中權值最小的邊。把e添加到樹中,e會和樹上從x到y的路徑一起構成一個環,並且環上其他邊的權值都比z大。因此,用e代替環上的其他任意一條邊,會形成一棵權值和更小的生成樹,與假設矛盾。故假設不成立,原命題成立。
推論:
給定一張無向圖G=(V,E),n=|V|,m=|E|。從E中選出k<n-1條邊構成G的加一個生成森林。若再從剩余的m-k條邊中選n-1-k條添加到生成森林中,使其成為G的生成樹,並且選出的邊的權值之和最小,則該生成樹一定包含這m-k條邊中連接生成森林的兩個不連通節點的權值最小的邊。
//以上來自《算法競賽 進階指南》
AcWing 1146. 新的開始
//建立一個“超級發電站”,將所有礦井與其都連上邊
AcWing 1145. 北極通訊網絡
//找一個最小的d值,使得將所有權值大於d的邊刪去之后,整個圖形的連通塊的個數不超過k。
//Kruskal算法:假設當前已經循環完第i條邊。已經求出了由前i條邊構成的連通塊。
//可以用DFS/BFS+二分,但用並查集的話則無需二分
AcWing 346. 走廊潑水節
//對於兩個連通塊:
- 新邊<w ×
- 新邊==w ×
- 新邊≥w+1 √
這樣才能保證圖的唯一最小生成樹仍是原樹。
AcWing 1148. 秘密的牛奶運輸
//次小生成樹
定義:給一個帶權的圖,把圖的所以生成樹按權值從小到大排序,第二小的稱為次小生成樹。
//非嚴格次小生成樹的邊權可以和最小生成樹相等,而嚴格次小生成樹則不可以。
定理:對於一張無向圖,如果存在最小生成樹和(嚴格)次小生成樹,那么對於人格一棵最小生成樹,都存在一棵(嚴格)次小生成樹,使得這兩棵樹只有一條邊不同。
方法1:先求最小生成樹,再枚舉刪去最小生成樹中的邊求解。時間復雜度
方法2:先求最小生成樹,然后依次枚舉非樹邊,然后將該邊加入樹中,同時從樹中去掉一條邊,使得最終的圖仍是一棵樹。則一定可以求出次小生成樹。
- 設T為圖G的一棵生成樹,對於非樹邊a和樹邊b,插入邊a,並刪除邊b的操作記為(+a,-b)。
- 如果T+a-b之后,仍然是一棵生成樹,稱(+a,-b)是T的一個可行交換。
- 稱由T進行一次可行變換所得到的新的生成樹集合為T的鄰集。
定理:次小生成樹一定在最小生成樹的鄰集中。
//以上來自《信息學奧賽一本通·提高篇》
要使得最小,就要使最大。可以枚舉n個點,以每一個點為樹根,復雜度預處理出每個點到其他所有點的路徑上的最大邊權。所以時間復雜
//也可以用樹鏈剖分進行預處理
1.求最小生成樹,標記每條邊是樹邊,還是非樹邊;同時把最小生成樹建立出來。
2.預處理任意兩點間的邊權最大值dist[a][b]
3.依次枚舉所有非樹邊,求,滿足w>dist[a][b]。
//注意:在求嚴格次小生成樹時,不能只預處理兩點之間最大的樹邊,因為當最大樹邊和當前枚舉的非樹邊長度相同時,就不能替換了,但此時卻可以替換長度次大的樹邊。因此還需同時預處理出長度次大的樹邊。
負環
01分數規划
求負環的常用方法,基於spfa:(基於抽屜原理)
- 統計每個店入隊的次數,如果某個點入隊n次,則說明存在負環
- 統計當前每個點的最短路中所包含的邊數,如果某點的最短路所包含的邊數大於等於n,則也說明存在環(一般選擇這種方法)
將所有點加入隊列中,並初始化dist[i]=0。此時可以看成由虛擬源點向所有點都連了一條邊,不影響負環的判斷。又因為無論dist數組初始化成0還是0x3f3f3f3f,有負環存在都會逐漸減為負無窮,所以初始化成任意值皆可。也沒必要memset
當所有點的入隊次數超過2n時,我們就認為圖中與很大可能是存在負環的。
AcWing 904. 蟲洞
AcWing 361. 觀光奶牛
//使最大,即01分數規划問題。
,可用二分。
將邊權重定義為,判斷是否存在正環不一定要把邊權取負,可以在spfa中改變判斷符號,變成求最長路。
//保留兩位小數,二分時>1e-4即可
同理,若保留三位小數,則>1e-5
AcWing 1165. 單詞環
//建圖:
以每個單詞的前兩個字母和后兩個字母為節點,以單詞的長度為邊,最多建立676個點、10⁵條邊
//同樣還是01分數規划問題,解法同上題
//優化:在循環時計數,當循環次數cnt>2*n(n為點數)時就可以直接認為存在正環。將隊列換成棧也可以。(但在普通題目中無需將隊列換成棧,因為棧的效率極低)
差分約束
(1) 求不等式組的可行解
源點需要滿足的條件:從源點出發,一定可以走到所有的邊。
步驟:
- 先將每個不等式xi<=xj+c轉化成一條從xj走到xi,長度為ck的一條邊
- 找一個超級源點,使得該源點一定可以遍歷到所有邊
- 從源點求一遍單源最短路
結果:
- 如果存在負環,則原不等式組一定無解
- 如果沒有負環,則dist[i]就是原不等式組的一個可行解
//最短路:所有從1->i的路徑的最小值
最長路:所有從1->i的路徑的最大值
(2) 如何求最大值或最小值,這里的最值指的是每個變量的最值
結論:
如果求的是最小值,則應該求最長路;如果求的是最大值,則應該求最短路。
以求xi的最大值為例:所有從xi出發,構成的不等式鏈xi<=xj+c1<=xk+c2+c1<=...<=c1+c2+...+ck所計算出的上界,最終xi的最大值等於所有上界的最小值。
同理,若是求最大值,則是求所有下界的最大值。
問題:
如何轉化xi<=c,其中c是一個常數,這類的不等式
方法:
建立一個超級源點,0,然后建立0->xi,長度是c的邊即可
AcWing 1169. 糖果
//最小值則求最長路
相對關系:
絕對關系:
//無解情況:是否存在正環
//邊數組大小開三倍N(最壞情況下都是A=B,需要建雙向邊,另外每個點都要和超級源點連一條邊)
//優化:如果用隊列會TLE幾個數據點,所以將隊列替換成棧
AcWing 362. 區間
//貪心或差分約束
首先將所有ai變成ai+1,bi變成bi+1,這樣數據范圍就變成了[1~50001],為了將0騰出來建立超級源點
//最小值則求最長路(dist數組初始化為-0x3f)
//前綴和思想:Si表示1~i中被選出的數的個數。
//注意:
AcWing 1170. 排隊布局
//最大值則求最短路
建立虛擬源點使得,即從虛擬源點向i號點連了一條邊。但在實際操作中無需真的建立出虛擬源點,將所有點都入隊即可。
//無解情況:判斷是否存在負環
//由於題目給定的是相對關系,所以可以令X1=0,那么題目所求的Xn-X1就是Xn,只要求出X1到所有點的最短路,如果Xn的值為正無窮,則說明 1 號奶牛和 N 號奶牛間的距離可以任意大,否則1 號奶牛和 N 號奶牛間可能的最大距離即Xn。
//本題需要進行兩次spfa,對於第一問,需將所有點都入隊,即spfa(n);對於第二問,只需求出X1到其他點的最短路,即spfa(1)。 //注意每次spfa前都要初始化:
cnt數組記錄當前路徑的邊數,即用來判斷是否存在負環。
AcWing 393. 雇佣收銀員
//最小值則最長路
//num[i]表示i時刻來的人數
X[i]表示最終從num[i]中挑出來的人數
,也是為了將0騰出來建立超級源點
//前綴和思想:令Si為Xi的前i項和,則
即
由於在最后一個式子中有三個變量,所以我們可以直接從小到大在0~1000內枚舉,如果找到符合條件的就直接輸出,若循環結束仍未找到則說明無解。
//在枚舉的過程中,為了體現
是個定值,即
最近公共祖先
- 向上標記法 O(n)
- 倍增法
fa[i,j]表示從i開始,向上走2^j步所能走到的節點。
depth[i]表示深度
//哨兵:如果從i開始跳2^j步會跳過根節點,那么fa[i,j]=0。 depth[o]=0
//步驟:(二進制拼湊法)
(1) 先將兩個點跳到同一層
(2) 讓兩個點同時往上跳,一直跳到它們的最近公共祖先的下一層。
預處理 O(nlogn) //廣搜,不會爆棧
查詢O(logn)
//j=0,f(i,j)=i的父節點
j>0,f(i,j)=f(f(i,j-1),j-1)
- Tarjan——離線求LCA O(n+m)
//本質上是對向上標記法的優化
在深度優先遍歷時,將所有點分成三大類:
1) 已經遍歷過,且回溯過的點
2) 正在搜索的分支
3) 還未搜索到的點
AcWing 1172. 祖孫詢問
//倍增求lca
AcWing 1171. 距離
//兩點之間的最短距離:d(x)+d(y)-2*d(lca(x,y))
//預處理出每個節點到根節點的距離d(i)
//用vector<PII>來存詢問,first存查詢的另外一個點,second存查詢編號
AcWing 356. 次小生成樹
//注意要用long long,會爆int
//思路和AcWing 1148. 秘密的牛奶運輸相似
//d1[i,j]表示i->j路徑上的最大邊
d2[i,j]表示i->j路徑上的次大邊
AcWing 352. 闇の連鎖
//樹上差分
給從x到y的路徑上的所有邊都加上c:
//最后枚舉每棵子樹:
有向圖的強連通分量
對於一個有向圖,連通分量:對於分量重任意兩點u,v,必然可以從u走到v,且從v走到u。
有向圖通過縮點(將所有連通分量縮成一個點)變成有向無環圖(DAG),即拓撲圖,便於求最短(或最長)路、遞推。時間復雜度為O(n+m)。
DFS(樹枝邊、前向邊:指向某個子孫、后向邊:指向某個祖宗、橫叉邊)
情況1:存在后向邊指向祖先節點
情況2:先走到橫叉邊,橫叉邊再走到祖先
Tarjan算法求強連通分量(SCC)
對每個點定義兩個時間戳:
dfn[u]表示遍歷到u的時間戳
low[u]從u開始走,所能遍歷到的最小時間戳是什么。
u是其所在的強連通分量的最高點,等價於dfn[u]==low[u]
//棧中放當前強聯通分量中還沒有搜完的所有點
- 縮點
for(i=1;i<=n;i++)
for(i的所有鄰點j)
if(i與j不在同一個SCC中)
加一條新邊 id(i)->id(j)
//連通分量編號遞減的順序一定是拓撲序
AcWing 1174. 受歡迎的牛
//先縮點,再在拓撲圖里找出出度為零的點。(如果有兩個或以上這樣的點,本題答案即為0)答案即這個點所代表的強連通分量中的點數。
AcWing 367. 學校網絡
//設縮點后有P個起點,Q個終點
//第一問即P
//第二問為max(P,Q)
假設|P|≤|Q|
1) |P|==1 ans=|Q|
2) |P|>1 |Q|≥|P|>1
必能找到兩個不同的起點到達不同的終點,設這兩個起點分別為p1、p2,終點分別為q1、q2。
//反證:如果找不到這樣的兩個點,說明所有起點都到達同一個終點q1。但|Q|>|P|,所以必然有q2是來自其中一個起點,得出矛盾。
此時給q1、p2連一條邊,則|P’|=|P|-1,|Q’|=|Q|-1。連|P|-1次這樣的邊,使得|P|=1,那么此時總共連的邊數為|Q|-(|P|-1)+|P|-1,即|Q|。
//第二問注意特判:如果只有一個強連通分量,則答案為0
AcWing 1175. 最大半連通子圖
//導出子圖:從原圖中選出一些點,再把所有跟這些點相關的邊都選出來(如果兩點間有多條邊,則應將這些邊全部選出)。
- Tarjan
- 縮點(不能有重邊)建圖,給邊判重
- 按拓撲序遞推
//第一問:求拓撲圖的最長鏈
//第二問:求最長鏈的方案數 DP
設f(i)為表示到i這個點的最長鏈的長度,g(i)表示到到i這個點的最長鏈的方案數,s(i)表示i這個強連通分量中的點數。
如果可以從j->i:
1.若f(j)+s(i)>f(i),則f(I)=f(j)+s(i),且g(i)=g(j)
2.若f(j)+s(i)==f(i),則g(i)+=g(j)
//因為連通分量編號遞減的順序一定是拓撲序,所以無需再次建圖,只要按照該順序DP即可。
AcWing 368. 銀河
//本題正解:Tarjan算法(穩定,時間復雜度可達線性)
- Tarjan
- 縮點
- 依據拓撲序遞推
//本題可以用01分數規划,思路類似AcWing 1169. 糖果 ,將隊列換成棧,仍有超時的風險。
另外還有兩個條件:
1.必須有絕對值
2.超級源點
//本題的特殊性:
邊權都為正,因此在判斷是否存在正環時,只要在某一個強連通分量中有某一條邊的權值大於0,就形成了一個正環(因為強聯通分量中的任意兩點都互相連通)。所以強連通分量中的所有邊的權值都要為0,即所有點最終的距離都相等。因此題目變成了在拓撲圖上,所有點到起點的最長距離。
//建立了超級源點后,就可以直接Tarjan(o)。
//本題需要用long long
無向圖的雙連通分量(也叫重連通分量)
邊雙連通分量e-DCC:極大的不包含橋的連通塊
//橋:刪去橋原圖不連通
//性質1:不管刪掉哪條邊,該圖仍然連通
性質2:任意兩點之間都存在兩條不相交的路徑
點雙連通分量v-DCC:極大的不包含割點的連通塊
//割點:刪去割點原圖不連通
//每一個割點都至少屬於兩個連通分量
//兩個割點之間的邊不一定是橋,橋的兩個端點也不一定是割點
一個點雙連通分量不一定是一個邊雙連通分量,同樣地,一個邊雙連通分量也不一定是一個點雙連通分量
邊雙連通分量:dfn(x)、low(x)
如何找到橋:
如何找到所有邊的雙連通分量:
- 將所有橋刪掉
- Stack dfn(x)==low(x)
如何求割點:low(y)≥dfn(i)
(1) 如果x不是根節點,那么x是割點
(2) x是根節點,至少有兩個子節點yi滿足low(yi)≥dfn(i)
如何求點雙連通分量:
1) 統計連通塊個數 cnt
2) 枚舉從哪個塊刪,再枚舉刪除哪個點
AcWing 395. 冗余路徑
//兩條路徑沒有一條重合的道路
//給定一個無向連通圖,問最少加幾條邊,可以將其變成一個邊雙連通分量。
//將所有邊雙連通分量縮點,原圖變成了一棵樹,再將所有葉子節點連接起來。(cnt為葉子節點的數量)
AcWing 1183. 電力
//求割點 stack
//多組數據記得memset(dfn,0,sizeof dfn)
//孤立點也是雙聯通分量
AcWing 396. 礦場搭建
//給定一個無向圖,問最少在幾個點上設置出口,可以使得不管哪個點坍塌,其余所有點都可以與某個出口連通。
- 出口數量≥2
- 分別看每個連通塊
1) 無割點
2) 有割點
- 縮點:
1.每個割點單獨作為一個點
2.從每個v-DCC向其所包含的每個割點連邊
- V-DCC度數為1:需在該分量內部(非割點)放一個出口
- V-DCC度數>1:無需設置出口
歐拉回路和歐拉路徑
哥尼斯堡七橋問題(一筆畫)
- 對於無向圖,所有邊都是連通的
(1) 存在歐拉路徑的充分必要條件:度數為奇數的點只能有0或2個。
(2) 存在歐拉回路的充分必要條件:度數為奇數的點只能有0個。
- 對於有向圖,所有邊都是連通的
(1) 存在歐拉路徑的充分必要條件:要么所有點的出度均等於入度;要么除了兩個點以外,其余所有點的出度等於入度,剩余的兩個點:一個滿足出度比入度多1(起點),另一個滿足入度比出度多1(終點)。
(2) 存在歐拉回路的充分必要條件:所有點的出度均等於入度。
實現方式:dfs,在dfs的最后將當前遍歷的節點加入隊列中
注意:用邊判重,時間復雜度可能會很高。可以在走過一條邊之后就將這條邊刪去。在無向圖中,由於建了雙向邊,兩條邊都要刪掉。雙向邊的編號為(1,2) (3,4) (5,6)......
AcWing 1123. 鏟雪車
//由於每個點的入度和出度都相等,從起點可以到任意一條街道,顯然本題就是一個歐拉回路。只要將所有街道的長度之和乘2,再除以鏟雪車的速度即可。
//注意答案輸出的單位轉化
//本題乍一看是一道圖論題,但實際上通過分析題目不難得出歐拉回路的結論。
AcWing 1184. 歐拉回路
//如何判斷無解:
- 無向圖:
(1) 所有點的度數必須是奇數
(2) 所有邊連通
- 有向圖:
(1) 所有點的入度等於出度
(2) 所有邊連通
AcWing 1124. 騎馬修柵欄
//保證dfs中for循環里u的所有出邊都按照從小到大的順序,這樣輸出的答案就會是字典序最小的。
AcWing 1185. 單詞游戲
//以字母為點(26個點),以單詞為邊
判斷有向圖是否存在歐拉路徑:
- 除了起點和終點外,其余點入度=出度
- 所有邊都連通(並查集)
拓撲排序
拓撲圖=有向無環圖
將所有入度為0的點入隊
對於任意一個點,其前驅點的個數都是有限的
AcWing 1191. 家譜樹
//每輸入一個孩子,就使孩子的入度++
//拓撲排序模板
AcWing 1192. 獎金
//差分約束求最長路
1) 邊權無限制 spfa O(nm)
2) 邊權非負 tarjan O(n+m)
3) 邊權>0 拓撲排序 O(n+m)
//用拓撲排序來做差分約束問題。每個點都最小,使得總和最小
AcWing 164. 可達性統計
//DP:
f(i):所有能從i到達的點的集合
集合用長度為n的二進制串表示(例如1011001......),並使用bitset函數
AcWing 456. 車站分級
//暴力建圖會超時
//告訴我們很多個a>b,要求最小的等級,即從b->a建立邊權為1的邊(很裸的查分約束問題)
1.拓撲排序
2.虛擬源點:將n^2的復雜度變成了線性
//若要連接兩個集合,可在集合之間建立一個虛擬源點,從左邊的集合中的每一個點向虛擬源點建立一條邊權為0的邊,再從虛擬源點向右邊集合中的每一個點建立一條邊權為1的邊
---------------------------------------------------->
在最后,感謝Gold_stein(xym)同學提供了自己的筆記供我參考 Thanks♪(・ω・)ノ