intdfs(int u){st[u]=true;// st[u] 表示點u已經被遍歷過for(int i = h[u]; i !=-1; i = ne[i]){int j = e[i];if(!st[j])dfs(j);}}
(2) 寬度優先遍歷 —— 模板題 AcWing 847. 圖中點的層次
queue<int> q;
st[1]=true;// 表示1號點已經被遍歷過
q.push(1);while(q.size()){int t = q.front();q.pop();for(int i = h[t]; i !=-1; i = ne[i]){int j = e[i];if(!st[j]){st[j]=true;// 表示點j已經被遍歷過q.push(j);}}}
拓撲排序
模板題 AcWing 848. 有向圖的拓撲序列 時間復雜度 O(n+m), n 表示點數,m 表示邊數
booltopsort(){int hh =0, tt =-1;// d[i] 存儲點i的入度for(int i =1; i <= n; i ++)if(!d[i])q[++ tt]= i;while(hh <= tt){int t = q[hh ++];for(int i = h[t]; i !=-1; i = ne[i]){int j = e[i];if(-- d[j]==0)q[++ tt]= j;}}// 如果所有點都入隊了,說明存在拓撲序列;否則不存在拓撲序列。return tt == n -1;}
樸素dijkstra算法
模板題 AcWing 849. Dijkstra求最短路 I
時間復雜是 O(n2+m), n表示點數,m 表示邊數
int g[N][N];// 存儲每條邊int dist[N];// 存儲1號點到每個點的最短距離bool st[N];// 存儲每個點的最短路是否已經確定// 求1號點到n號點的最短路,如果不存在則返回-1intdijkstra(){memset(dist,0x3f,sizeof dist);dist[1]=0;for(int i =0; i < n -1; i ++){int t =-1;// 在還未確定最短路的點中,尋找距離最小的點for(int j =1; j <= n; j ++)if(!st[j]&&(t ==-1|| dist[t]> dist[j]))t = j;// 用t更新其他點的距離for(int j =1; j <= n; j ++)dist[j]=min(dist[j], dist[t]+ g[t][j]);st[t]=true;}if(dist[n]==0x3f3f3f3f)return-1;return dist[n];}
堆優化版dijkstra
模板題 AcWing 850. Dijkstra求最短路 II 時間復雜度 O(mlogn), n 表示點數,m 表示邊數
constint N=510;constint M=1e4+10;int n, m;// n表示點數,m表示邊數int dist[N],backup[N];// dist[x]存儲1到x的最短路距離structEdge// 邊,a表示出點,b表示入點,w表示邊的權重{int a, b, w;}edges[M];// 求1到n的最短路距離,如果無法從1走到n,則返回-1。intbellman_ford(){memset(dist,0x3f,sizeof dist);dist[1]=0;// 如果第n次迭代仍然會松弛三角不等式,就說明存在一條長度是n+1的最短路徑,//由抽屜原理,路徑中至少存在兩個相同的點,說明圖中存在負權回路。for(int i =0; i < n; i ++){memcpy(backup,dist,sizeof dist);for(int j =0; j < m; j ++){int a = edges[j].a, b = edges[j].b, w = edges[j].w;dist[b]=min(dist[b],backup[a]+w);}}if(dist[n]>0x3f3f3f3f/2)return-1;return dist[n];}
初始化:for(int i =1; i <= n; i ++)for(int j =1; j <= n; j ++)if(i == j) d[i][j]=0;else d[i][j]= INF;// 算法結束后,d[a][b]表示a到b的最短距離voidfloyd(){for(int k =1; k <= n; k ++)for(int i =1; i <= n; i ++)for(int j =1; j <= n; j ++)d[i][j]=min(d[i][j], d[i][k]+ d[k][j]);}
樸素版prim算法
模板題 AcWing 858. Prim算法求最小生成樹 時間復雜度是 O(n2+m), n 表示點數,m 表示邊數
int n;// n表示點數int g[N][N];// 鄰接矩陣,存儲所有邊int dist[N];// 存儲其他點到當前最小生成樹的距離bool st[N];// 存儲每個點是否已經在生成樹中// 如果圖不連通,則返回INF(值是0x3f3f3f3f), 否則返回最小生成樹的樹邊權重之和intprim(){memset(dist,0x3f,sizeof dist);int res =0;for(int i =0; i < n; i ++){int t =-1;for(int j =1; j <= n; j ++)if(!st[j]&&(t ==-1|| dist[t]> dist[j]))t = j;if(i && dist[t]== INF)return INF;//說明不連通if(i) res += dist[t];//第一次迭代也就是第一個點的距離不用加st[t]=true;for(int j =1; j <= n; j ++) dist[j]=min(dist[j], g[t][j]);}return res;}
Kruskal算法
模板題 AcWing 859. Kruskal算法求最小生成樹 時間復雜度是 O(mlogm), n 表示點數,m 表示邊數
int n, m;// n是點數,m是邊數int p[N];// 并查集的父節點數組structEdge// 存儲邊{int a, b, w;booloperator<(const Edge &W)const{return w < W.w;}}edges[M];intfind(int x)// 并查集核心操作{if(p[x]!= x) p[x]=find(p[x]);return p[x];}intkruskal(){sort(edges, edges + m);for(int i =1; i <= n; i ++) p[i]= i;// 初始化并查集int res =0, cnt =0;for(int i =0; i < m; i ++){int a = edges[i].a, b = edges[i].b, w = edges[i].w;a =find(a), b =find(b);if(a != b)// 如果兩個連通塊不連通,則將這兩個連通塊合并{p[a]= b;res += w;cnt ++;}}if(cnt < n -1)return INF;return res;}
染色法判別二分圖
模板題 AcWing 860. 染色法判定二分圖 時間復雜度是 O(n+m), n 表示點數,m 表示邊數
int n;// n表示點數int h[N], e[M], ne[M], idx;// 鄰接表存儲圖int color[N];// 表示每個點的顏色,-1表示未染色,0表示白色,1表示黑色// 參數:u表示當前節點,c表示當前點的顏色booldfs(int u,int c){color[u]= c;for(int i = h[u]; i !=-1; i = ne[i]){int j = e[i];if(color[j]==-1){if(!dfs(j,!c))returnfalse;//染色沖突了}elseif(color[j]== c)returnfalse;//染色沖突了}returntrue;}boolcheck(){memset(color,-1,sizeof color);bool flag =true;for(int i =1; i <= n; i ++)if(color[i]==-1)if(!dfs(i,0)){flag =false;break;}return flag;}
匈牙利算法 (二分圖匹配)
模板題 AcWing 861. 二分圖的最大匹配 時間復雜度是 O(nm), n 表示點數,m 表示邊數
int n1, n2;// n1表示第一個集合中的點數,n2表示第二個集合中的點數int h[N], e[M], ne[M], idx;// 鄰接表存儲所有邊,匈牙利算法中只會用到從第一個集合指向第二個集合的邊,所以這里只用存一個方向的邊int match[N];// 存儲第二個集合中的每個點當前匹配的第一個集合中的點是哪個bool st[N];// 表示第二個集合中的每個點是否已經被遍歷過boolfind(int x){for(int i = h[x]; i !=-1; i = ne[i]){int j = e[i];if(!st[j]){st[j]=true;if(match[j]==0||find(match[j])){match[j]= x;returntrue;}}}returnfalse;}// 求最大匹配數,依次枚舉第一個集合中的每個點能否匹配第二個集合中的點int res =0;for(int i =1; i <= n1; i ++){memset(st,false,sizeof st);if(find(i)) res ++;}