1 并查集模板函数
int a[100]; //初始状态,每个点的父亲是自己或者0,即每个点各是一个集合。 int InitSet(int MemberNum) { for(int i=0;i<MemberNum;i++) a[i]=i; /* for(int i=0;i<=MemberNum-1;i++) a[i]=0; */ } int rootfind(int x) { if(a[x]==x) return (x); else a[x]=rootfind(a[x]); return (a[x]); //或者 return(a[x]==x?x:a[x]=rootfind(a[x])) } //合并两个根节点,即合并两个集合。先找根节点,在调用此函数, //也可以不用函数 void UnionRoot(int r1,int r2) { a[r2] = r1; }
struct union_find_set { //超级简洁的并查集 //一个并查集结构体 面向对象的思想 将变量和函数封装起来。实现了并查集的初始化,找祖先和合并操作 int n,fa[MAXN]; void init(int N) { n=N; for (int i=1;i<=n;i++) fa[i]=i; } int getFather(int u) { while (u=fa[u]=fa[fa[u]],u!=fa[u]); return u; } bool set_union(int u,int v) { return (u=getFather(u))==(v=getFather(v)) ? false : fa[v]=u; } } ufs;
2 最短路径算法
Dijkstra、spfa、foyd、Bellman-Ford四种算法的比较。
Dijkstra:求单源最短路径,仅适用于正权图,时间复杂度:o(n2),可用小根堆优化
Bellman-Ford:求单源最短路径,适用于正权图和负权图,判断并找出负权回路。时间复杂度:o(NM)
Spfa(Bellman-Ford的队列优化算法): 求单源最短路径,适用于正权图和负权图,判断并找出负权回路,时间复杂度:o(VM)
Foyd:求多源最短路径,适用于正权图,时间复杂度:o(n3)
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> using namespace std; struct Edge{ int to,w; }; vector<Edge>E[1010];//下标从1开始 int dist[1010];//下标从1开始 bool used[1010];//下标从1开始 const int MAX=1000000010; int main() { int i,j,u,v,t,n,m,s; scanf("%d%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&t); E[u].push_back((Edge){v,t}); E[v].push_back((Edge){u,t}); //有向图版:E[u].push_back((Edge){v,t}); //无向图要存两条边 } for(i=1;i<=n;i++) dist[i]=MAX; dist[1]=0;//源点为1 int k,Min; for(i=0;i<n-1;i++)//迪杰斯特拉算法,这里控制循环次数,必须保证n-1次循环 { //在没确定最短路径的点中寻找路径最短的点,将它作为下一步的中转点 Min=MAX; for(j=1;j<=n;j++) if(Min>dist[j] && used[j]==0) { k=j; Min=dist[j]; } s=k; used[s]=1; //松弛 for(j=1;j<E[s].size();j++) dist[E[s][j].to]=min(dist[E[s][j].to],dist[s]+E[s][j].w); } printf("%d",dist[n]); }
2.2迪杰斯特拉算法优化
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> using namespace std; const int N=100010; struct Edge{int to,len;}; vector <Edge> E[N]; int n,m; long long dist[N]; bool used[N]; struct Point{ int no; long long dist; friend bool operator > (const Point A, const Point B){ return A.dist!=B.dist?A.dist>B.dist:A.no>B.no; } }; priority_queue <Point, vector<Point>, greater<Point> > Q; void Dijk(int st){ memset(dist, 0x3f, sizeof(dist)); dist[st]=0ll; Q.push((Point){st,0ll}); while(!Q.empty()){ int now=Q.top().no; Q.pop(); if(used[now]) continue; used[now]=1; for(int i=0;i<E[now].size();i++){ if(dist[E[now][i].to]>dist[now]+E[now][i].len){ dist[E[now][i].to]=dist[now]+E[now][i].len; Q.push((Point){E[now][i].to, dist[E[now][i].to]}); } } } } int main(){ int S; scanf("%d%d%d", &n,&m,&S); for(int i=0;i<m;i++){ int s,t,d; scanf("%d%d%d", &s,&t,&d); E[s].push_back((Edge){t,d}); } Dijk(S); for(int i=1;i<=n;i++) printf(i<n?"%lld ":"%lld ", dist[i]); return 0; }
2.3 弗洛伊德算法
#include <iostream> using namespace std; const int INF=1e9; int N, M, dist[1010][1010]; void floyd(){ for(int k=1;k<=N;k++) for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]); } int main(){ scanf("%d %d", &N, &M); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) dist[i][j]=(i==j?0:INF); for(int i=0;i<M;i++){ int s,t,d; scanf("%d %d %d", &s, &t, &d); dist[s][t]=min(dist[s][t], d); } floyd(); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) printf(j<N?"%d ":"%d ", dist[i][j]); return 0; }
2.4 SPFA
void spfa() { memset(used,0,sizeof(used)); memset(dist,0x3f,sizeof(dist)); int i,s; queue <int> q; used[1]=1; q.push(1); dist[1]=0; while(!q.empty()) { s=q.front(); q.pop(); used[s]=0;//队头出队,并将队头used标记设为0,即不在队中 for(i=0;i<E[s].size();i++) if( dist[s]+E[s][i].w<dist[E[s][i].to])//用刚出队的点作为中转点即松弛点,松弛它的所有邻接点。 { dist[E[s][i].to]=dist[s]+E[s][i].w; if(used[E[s][i].to]==0)//被更新的邻接点如果不在队中,则入队并改变是否在队列中的标记 { q.push(E[s][i].to); used[E[s][i].to]=1; /* //以下几行是为了判断环:若一个点入队的次数等于n次,则存在负权环 times[i]++; if(times[i]>=n) { printf("-1"); return 0; } */ } } } }