并查集
核心代码
void make_set(){
for(int x = 1; x <= n; x++)
parent[x]=x;
}
int find_set(int x){
if(x!=parent[x])
parent[x] = find_set(parent[x]);
return parent[x];
}
void union_set(int x,int y){
x=find_set(x);
y=find_set(y);
if(x==y)return;
parent[y]=x;
return;
}
例题
一个学校里有n个人,其中有m对人是具有相同宗教信仰,请问这个学校里信仰的宗教最多有多少个?
(这个示例代码没有解决当输入的两个数前一个大后一个小的情况)
#include<stdio.h>
#include<iostream>
using namespace std;
int m,n;
int parent[200];
void make_set(){
for(int x = 1; x <= n; x++)
parent[x]=x;
}
int find_set(int x){
if(x!=parent[x])
parent[x] = find_set(parent[x]);
return parent[x];
}
void union_set(int x,int y){
x=find_set(x);
y=find_set(y);
if(x==y)return;
parent[y]=x;
return;
}
int main(){
cin>>n>>m;
int a;
make_set();
for(a=1;a<m+1;a++){
int i,j;
cin>>i>>j;
union_set(i,j);
for(int i=1;i<n+1;i++)
cout<<"parent["<<i<<"]="<<parent[i]<<endl;
}
int ans=0;
for(int i=1;i<n+1;i++){
if(parent[i]==i)
ans++;
}
printf("%d\n",ans);
for(int i=1;i<n+1;i++)
cout<<"parent["<<i<<"]="<<parent[i]<<endl;
}
最小生成树
概念
一个有n个结点的连通图的生成树是原图的极小联通子图,期包含原图的所有n个结点,且有保持图连通的最少边。
最小生成树其实就是最小权重生成树的简称。
kruskal算法
思想
找最小的边,且每次找到的边不会和以前找出的边形成环路,直到所有顶点被找到。
过程
- 将图各边按照权值排序;
- 将图遍历一边,找出权值最小的边,若不能成环则加入到集合中,负责继续遍历图,寻找下一个最小的边;
- 递归重复步骤一,直到找出n-1条边。
示例代码
并查集:
用二维数组:(麻烦,而且没写全)
#include<stdio.h>
#include<iostream>
#include<vector>
using namespace std;
int m,n;
int isconnect(int x, int y ,int a[][]){
//判断是否能与原来标记的点构成环形回路
}
int main(){
cin>>n;
int dis[n][n];
int vis[n][n];
for(int f=0;f<n;f++){
v[f]=1;
}
while(1){
int i,j,d;
cin>>i>>j>>d;
if(i=j=0)break;
dis[i][j]=d;
vis[i][j]=1;
}
while(1)
{
int min=0x3f3f3f3f;
int mini,minj;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(vis[i][j]&&(dis[i][j]<min))
{
min=dis[i][j];
mini=i;
minj=j;
}
}
if(isconnect(mini,minj,vis)){
dis[i][j]=0x3f3f3f3f;
continue;
}
vis[i][j]=0;
v[i]=v[j]=0;
for(int f=0;f<n;f++){
if(v[f])continue;
}
break;
}
}
Prim算法
基本原理
重复下列操作直到Vnew = V:
1.在集合E中选取权值最小的边(u,v),其中u为集合Vnew中的元素而v不在Vnew集合中,并且v属于V;
2.将v加入集合Vnew中将(u,v)加入Enew中。
示例代码