Kruskal算法 :(并查集)
时间复杂度O(elog2e),适合简单图。
算法步骤:
1.构造一个有n个顶点的无边子图;
2.从原图选择边权最小的边加入该子图,直至子图成为一棵树;
3.边能加入子图的条件是,边的两个端点u,v还未连通,Kruskal算法中运用并查集的查询来询问两个顶点是否连通;
Kruskal算法的本质是,通过树的合并(不断加边,构成子树),来构建完整的生成树。
就是先将边的权按从小到大的顺序排起来,依次选出边,判断两点是否在一个集合中,如果在,看下一条边,如果不在,则将两点合并,这条边为最小生成树的一条边。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 505;
int a[MAXN][MAXN];
typedef struct graph{
int u,v,cost;
friend bool operator <(const graph &a,const graph &b)
{
return a.cost<b.cost;
}
};
graph edge[MAXN*MAXN];
int b[MAXN];
int c[MAXN];
int find2(int x)
{
int i = x;
while(x != b[x])
{
x = b[x];
}
int p;
while(i != b[i])
{
p = b[i];
b[i] = x;
i = p;
}
return x;
}
void find1(int n,int m)
{
int p = find2(n);
int q = find2(m);
if(p!=q)
{
b[q]=b[p];
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n,i,j;
scanf("%d",&n);
for(i = 1;i <= n;++i)
{
b[i] = i;
}
for(i=1;i<=n;++i)
{
for(j=1;j<=n;++j)
{
scanf("%d",&a[i][j]);
}
}
int k=1;
for(i = 1;i <= n;++i)
{
for(j = i + 1;j <= n;++j)
{
edge[k].u = i;
edge[k].v = j;
edge[k].cost = a[i][j];
++k;
}
}
sort(edge+1,edge+k);
/*for(i = 1;i < k;++i)
{
printf("%d %d %d\n",edge[i].u,edge[i].v,edge[i].cost);
}*/
int ptr = 0;
for(i = 1;i < k;++i)
{
if(find2(edge[i].u)!=find2(edge[i].v))
{
find1(edge[i].u,edge[i].v);
c[ptr++] = i;
}
}
int mx = 0;
for(i = 0;i < ptr;++i)
{
mx = (mx < edge[c[i]].cost)?edge[c[i]].cost:mx;
}
printf("%d\n",mx);
}
return 0;
}
Prim算法:(离散书上讲的方法)
时间是复杂度O(n2),适合稠密图。
Prim算法的基本步骤:
1.初始化点集 V={x};
2.找到边(u,v)满足:u∈点集V,v不∈点集V;
3.选取2.中满足条件的边中最小的一条加入生成树,并把v加入点集V,重复执行,直至原图所有的点都加入点集V,就得到了一棵最小生成树。
Tips:关于如何快速找到可以添加到生成树的边:
可以维护一个数组lowcost[i…j]记录点集V到各个顶点的最小边,即可快速找到边,且每当有新的点加入点集V时,该数组都要更新一次。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 2005;
const int inf = 0x3f3f3f3f;
int mp[MAXN][MAXN];
int lowcost[MAXN];
bool vis[MAXN];
int Prime(int n)
{
int i,j;
for(i = 1;i <= n;++i)
{
lowcost[i] = mp[1][i];
}
vis[1] = true;
lowcost[1] = 0;
int ans = 0;
for(i = 0;i < n - 1;++i)
{
int min = inf;
int k = 0;
for(j = 1;j <= n;++j)
{
if(!vis[j]&&min > lowcost[j])
{
min = lowcost[j];
k = j;
}
}
vis[k] = true;
ans = (ans > min)?ans:min;
for(j = 1;j <= n;++j)
{
if(!vis[j]&&lowcost[j] > mp[k][j])
lowcost[j] = mp[k][j];
}
}
return ans;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
int i,j;
memset(mp,inf,sizeof(mp));
memset(vis,false,sizeof(vis));
for(i = 0;i < m;++i)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
if(mp[x][y] > z)//防止出现重边现象
mp[x][y] = z;
if(mp[y][x] > z)
mp[y][x] = z;
}
int ans = Prime(n);
printf("%d\n",ans);
}
return 0;
}