注:我在修炼当中,博客完全是为了自己复习方便看得,如果你不慎点入了我的博客,看看就好,不要相信,误人子弟就不好了- -
最小生成树有Prim 和 Kruskal 两种经典算法 ,这两个呢都是利用MST性质来构造最小生成树的算法的,如果忘记了MST,去翻我的博客。
先说说prim算法,有个V点集,从中先选出一个点u0放入U点集中,然后找到一条与u0相连代价最小的边,且该边另一个点v0在V - U点集中。此时将该边加入最小生成树的边的集合中,然后将v0加入到U点集中,之后不断重复该操作,直到所有点都加入到了U点集中。
不会算时间复杂度,GG,T_T,大概是O(nloge)的,注意优先队列时间复杂度为O(logn)的
#include <iostream>
#include <queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef struct
{
long v;
long next;
long cost;
}Edge;
typedef struct
{
long v;
long cost;
}node;
bool operator <(const node &a,const node &b)
{
return a.cost>b.cost;
}
priority_queue<node> q;
const long MAXN=10000;
Edge e[MAXN];
long p[MAXN];
bool vist[MAXN];
long m,n;
long from,to,cost;
void init()
{
memset(p,-1,sizeof(p));
memset(vist,0,sizeof(vist));
while (!q.empty())
q.pop();
long i;
long eid=0;
for (i=0;i<n;++i)
{
scanf("%ld %ld %ld",&from,&to,&cost);
e[eid].next=p[from];
e[eid].v=to;
e[eid].cost=cost;
p[from]=eid++;
//以下适用于无向图
swap(from,to);
e[eid].next=p[from];
e[eid].v=to;
e[eid].cost=cost;
p[from]=eid++;
}
}
void Prim()
{
long cost=0;
init();
node t;
t.v=from;//选择起点
t.cost=0;
q.push(t);
long tt=0;
while (!q.empty()&&tt<m)
{
t=q.top();
q.pop();
if (vist[t.v])
{
continue;
}
cost+=t.cost;
++tt;
vist[t.v]=true;
long j;
for (j=p[t.v];j!=-1;j=e[j].next)
{
if (!vist[e[j].v])
{
node temp;
temp.v=e[j].v;
temp.cost=e[j].cost;
q.push(temp);
}
}
}
printf("%ld\n",cost);
}
int main()
{
while (scanf("%ld %ld",&m,&n)!=EOF) //m 代表点 n代表边数
{
Prim();
}
return 0;
}
时间复杂度是O(n2),与边数无关,适合求边稠密的网。
int minTree(int n)
{
int i,j;
memset(v,0,sizeof(v));
v[0]=1;
sum=0;
for(i=1;i<n;i++)
{
min=max;
for(j=0;j<n;j++)
{
if(!v[j]&&map[0][j]<min)
{
min=map[0][j];
flag=j;
}
}
sum+=min;
v[flag]=1;
for(j=0;j<n;j++)
{
if(!v[j]&&map[0][j]>map[flag][j])
{
map[0][j]=map[flag][j];
}
}
}
return sum;
}
#include <iostream>
using namespace std;
#define MAX 101
#define INF 999999999
#define max(a,b) (a>b?a:b)
int dis[MAX], pre[MAX];
int edge[MAX][MAX];
int maxVal[MAX][MAX];
bool inTree[MAX][MAX];
bool vis[MAX];
int Prime ( int n )
{
int i, j, k, minc, mst;
for ( i = 1; i <= n; i++ )
{
dis[i] = edge[1][i];
vis[i] = false;
pre[i] = 1;
}
dis[1] = mst = 0;
vis[1] = true;
for ( i = 2; i <= n; i++ )
{
minc = INF; k = -1;
for ( j = 1; j <= n; j++ )
{
if ( ! vis[j] && dis[j] < minc )
{
minc = dis[j];
k = j;
}
}
if ( minc == INF ) return -1; // 图不连通,没有找到最小生成树
mst += minc;
vis[k] = true;
inTree[pre[k]][k] = inTree[k][pre[k]] = true; // 记录加入的树中的边
for ( j = 1; j <= n; j++ )
if ( vis[j] == true )
maxVal[j][k] = max ( maxVal[j][pre[k]], edge[pre[k]][k] ); // 找j-k的路径上权值最大的那条边,并记录在maxVal[j][k]中
for ( j = 1; j <= n; j++ )
{
if ( ! vis[j] && dis[j] > edge[k][j] )
{
dis[j] = edge[k][j];
pre[j] = k; // 修正前驱
}
}
}
return mst;
}
void initial ( int n )
{
for ( int i = 1; i < n; i++ )
{
for ( int j = i + 1; j <= n; j++ )
{
edge[i][j] = edge[j][i] = INF;
inTree[i][j] = inTree[j][i] = 0;
maxVal[i][j] = maxVal[j][i] = 0;
}
}
}
int main()
{
int t, n, m, u, v, w;
scanf("%d",&t);
while ( t-- )
{
scanf("%d%d",&n,&m);
initial ( n );
while ( m-- )
{
scanf("%d%d%d",&u,&v,&w);
edge[u][v] = edge[v][u] = w;
}
int mst = Prime ( n );
if ( mst < 0 ) { printf("0\n"); continue; }
int res;
bool flag = false;
for ( int i = 1; i < n; i++ )
{
for ( int j = i + 1; j <= n; j++ )
{
if ( inTree[i][j] || edge[i][j] == INF ) continue; // 边edge[i][j]在树中或者i,j之间无边
res = mst + edge[i][j] - maxVal[i][j]; // 用边edge[i][j], 替换i-j路径上权值最大的那条边,得到一棵新的生成树
if ( res == mst ) { flag = true; break; }
}
if ( flag ) break;
}
if ( flag )
printf("Not Unique!\n");
else
printf("%d\n",mst);
}
return 0;
}
该算法是初始时所有点自成一个连通分量,然后在所有边中选择一条权重最小的边放入边集中,然后再寻找下一条最小的边,并且将该边加入边集的条件是该边
时间复杂度为O(eloge),适合边稀疏的网
#include<iostream>//这里的n代表节点数,给节点初始化,m代表边数,需要给边数排序,
#include<stdio.h>
#include<string.h>//kruskal算法主要用的是并查集
#include<algorithm>
using namespace std;
int inf=999999;
int fa[110000];
int n,m;
struct node
{
int u,v,w;
}q[110000];
void make_set()
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
}
int Find(int x)
{
if(x!=fa[x])
fa[x]=Find(fa[x]);
return fa[x];
}
bool cmp(node x,node y)
{
return x.w<y.w;
}
int kru()
{ int ans=0,cnt=0;
make_set();
sort(q+1,q+m+1,cmp);//排序要从起始地址,到终止地址此题中i是从1开始的,
for(int i=1;i<=m;i++)
{
int fx=Find(q[i].u);
int fy=Find(q[i].v);
if(fx!=fy)
{
fa[fx]=fy;
ans+=q[i].w;
cnt++;
}
}
return ans;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w);
}
printf("%d\n",kru());
}
return 0;
}