int n, m;// n表示点数,m表示边数int dist[N];// dist[x]存储1到x的最短路距离struct Edge // 边,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 ++){for(int j =0; j < m; j ++){int a = edges[j].a, b = edges[j].b, w = edges[j].w;if(dist[b]> dist[a]+ w)
dist[b]= dist[a]+ w;}}if(dist[n]>0x3f3f3f3f/2)return-1;return dist[n];}
spfa 算法(队列优化的Bellman-Ford算法)
时间复杂度 平均情况下 O(m),最坏情况下 O(nm), n 表示点数,m 表示边数
int n;// 总点数int h[N], w[N], e[N], ne[N], idx;// 邻接表存储所有边int dist[N];// 存储每个点到1号点的最短距离bool st[N];// 存储每个点是否在队列中// 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1intspfa(){memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int> q;
q.push(1);
st[1]=true;while(q.size()){auto t = q.front();
q.pop();
st[t]=false;for(int i = h[t]; i !=-1; i = ne[i]){int j = e[i];if(dist[j]> dist[t]+ w[i]){
dist[j]= dist[t]+ w[i];if(!st[j])// 如果队列中已存在j,则不需要将j重复插入{
q.push(j);
st[j]=true;}}}}if(dist[n]==0x3f3f3f3f)return-1;return dist[n];}
spfa判断图中是否存在负环
时间复杂度是 O(nm), n 表示点数,m 表示边数
int n;// 总点数int h[N], w[N], e[N], ne[N], idx;// 邻接表存储所有边int dist[N], cnt[N];// dist[x]存储1号点到x的最短距离,cnt[x]存储1到x的最短路中经过的点数bool st[N];// 存储每个点是否在队列中// 如果存在负环,则返回true,否则返回false。boolspfa(){// 不需要初始化dist数组// 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点,由抽屉原理一定有两个点相同,所以存在环。
queue<int> q;for(int i =1; i <= n; i ++){
q.push(i);
st[i]=true;}while(q.size()){auto t = q.front();
q.pop();
st[t]=false;for(int i = h[t]; i !=-1; i = ne[i]){int j = e[i];if(dist[j]> dist[t]+ w[i]){
dist[j]= dist[t]+ w[i];
cnt[j]= cnt[t]+1;if(cnt[j]>= n)returntrue;// 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环if(!st[j]){
q.push(j);
st[j]=true;}}}}returnfalse;}
floyd算法
时间复杂度是 O(n3), 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算法
时间复杂度是 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算法
时间复杂度是 O(mlogm), n 表示点数,m 表示边数
int n, m;// n是点数,m是边数int p[N];// 并查集的父节点数组struct Edge // 存储边{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;}
染色法判别二分图
时间复杂度是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;}
匈牙利算法
时间复杂度是 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 ++;}