[图论]最小生成树
Prim
注意
- 最好用
Prim_Heap
,相同条件下更快
- 考虑数据范围,树的重量可能会爆
int
说明
- 稀疏图较快
- 找对于到每一个点最近边的长度,存在对应下标的dis数组中
使用方法
Prim p;
p.init(Vsz, source);
初始化顶点个数,起始点位置
p.adde(u, v, w);
从u指向v权值为w的边,加一次边即可
int weight = p.prim();
输出最小生成树的重量,无法建成则返回-1
模板
#define Vmax 999
#define INF 0x3f3f3f3f
struct Prim
{
int n;//n个顶点
int s;//起点 树的根
int weight;//树的重量
int mp[Vmax][Vmax];
int dis[Vmax]; //dis[i]表示指向i点的最短的边
bool vis[Vmax];
int pre[Vmax];//记录前驱
void init(int Vsz,int source=1)//默认起点为1
{
n=Vsz;
weight=0;
for(int i=0;i<=n;i++)
{
dis[i]=INF;
vis[i]=false;
}
dis[source]=0;
}
//1~n的邻接矩阵
void adde(int u,int v,int w)
{
mp[u][v]=w;
mp[v][u]=w;
}
int prim()
{
int cnt=0;
for(int i=1;i<=n;i++)
{
int pos=0;
for(int j=1;j<=n;j++)
if(!vis[j]&&dis[j]<dis[pos])
pos=j;
vis[pos]=true;cnt++;
weight+=dis[pos];
for(int j=1;j<=n;j++)
if(!vis[j]&&mp[pos][j]<dis[j])
{
dis[j]=mp[pos][j];
pre[j]=pos;
}
}
if(cnt==n) return weight;
else return -1;
}
};
Prim-Heap
使用方法
- `init();
`
mytype ans = prim_heap(s)
得到以s为起点开始找的最小生成树重量
judge(n)
判断该树能否生成
模板
typedef int mytype;
const int NV=105;
const int NE=10005*2;
mytype dis[NV];
int pre[NV],vis[NV],he[NV],ecnt,pcnt;
struct edge
{
int v,next;
mytype l;
}E[NE];
void adde(int u,int v,mytype l)
{
E[++ecnt].v=v;
E[ecnt].l=l;
E[ecnt].next=he[u];
he[u]=ecnt;
}
void init(int n,int m,int s)
{
ecnt=0;
memset(pre,0,sizeof(pre));
memset(vis,0,sizeof(vis));
memset(he,-1,sizeof(he));
for (int i=0; i<=n; i++)
dis[i]=inf;
dis[s]=0;
for (int i=1; i<=m; i++)
{
int u,v;
mytype l;
scanf("%d%d%d",&u,&v,&l);
adde(u,v,l);
adde(v,u,l);
}
}
struct point
{
int u;
mytype l;
point(int u,mytype l):u(u),l(l){}
bool operator<(const point &p) const
{
return l>p.l;
}
};
mytype prim_heap(int s)
{
priority_queue<point> q;
q.push(point(s,0));
mytype ans=0;
pcnt=0;
while(!q.empty())
{
point p=q.top();
q.pop();
int u=p.u;
if (vis[u])
continue;
vis[u]=1;
ans+=p.l;//==dis[x]
pcnt++;
for(int i=he[u]; i!=-1; i=E[i].next)
if(!vis[E[i].v]&&E[i].l<dis[E[i].v])
{
dis[E[i].v]=E[i].l;
pre[E[i].v]=u;
q.push(point(E[i].v,dis[E[i].v]));
}
}
return ans;
}
bool judge(int n)
{
return pcnt==n;
}
Kruskal
说明
- 结构体排序+并查集———结构体排序找到所有符合条件的边,加入并查集
- 稀疏图较快
使用方法
- `Kruskal d;
`
d.init(Vsz,Esz);
初始化顶点个数,边的个数
d.adde(u,v,w);
从u指向v权值为w的边,单向加边即可
int weight = d.kruskal();
输出最小生成树的重量,无法建成则返回-1
模板
#define Vmax 300 //最大点数量
#define Emax 1000 //最大边数量
struct Kruskal
{
int n,m;//n个点 m条边
int rank[Vmax],fa[Vmax];//并查集需要
int weight;// weight为最小生成树的重量
int ecnt;
struct edge
{
int u,v,w;//起点 终点 权值
bool operator<(const edge e) const{return w<e.w;}
}e[Emax];
void init(int Vsz,int Esz)
{
n=Vsz;m=Esz;
ecnt=0;
weight=0;
for(int i=0;i<=Vsz;i++)
{
fa[i]=i;
rank[i]=0;
}
}
//边的编号从0~m-1
void adde(int u,int v,int w)
{
e[ecnt].u=u;
e[ecnt].v=v;
e[ecnt++].w=w;
}
int findx(int x)
{
while(x!=fa[x])
x=fa[x];
return x;
}
void set_merge(int u,int v)
{
if(rank[u]<rank[v])
{
fa[u]=v;
rank[v]+=rank[u];
}
else
{
fa[v]=u;
rank[u]+=rank[v];
}
}
int kruskal()
{
int cnt=0;
sort(e,e+m);
for(int i=0;i<m;i++)
{
int uR=findx(e[i].u);
int vR=findx(e[i].v);
if(uR!=vR)
{
set_merge(uR, vR);
weight+=e[i].w;
cnt++;
}
}
if(cnt==n-1) return weight;//最小生成树可建
else return -1; //最小生成树不可建
}
};