常用算法模板程序集锦 第一集:并查集 最短路径
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\n", 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\n", 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;
}
*/
}
}
}
}