近两周学习了并查集和拓扑排序。课上老师讲了具体的实现思想,并且通过题目直观地感受了并查集和拓扑排序的应用。
并查集:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。并查集模板主要由一个整型数组pre[ ]和两个函数find( )、join( )构成。
数组 pre[ ] 记录了每个点的前驱节点。
for(int i=0;i<n;i++)//n表示输入的结点个数
{pre[i]=i;//将每一个结点的前导点设为自己
}
函数 find(x) 用于查找指定节点 x 属于哪个集合。路径压缩为了加快查找速度,将x点与其根节点直接相连,构造成类似于只有叶子结点没有分支的树
int find(int x)
{while(pre[x]!=x)
x=pre[x];
int i=x, j;
while(i!=x)//路径压缩算法
{ j=pre[i];//记录x前导结点
pre[i]=x;//将i的前导结点设置为x根节点
i=j;
}
return x;
}
函数 join(x,y) 用于合并两个节点 x 和 y 。
void join(int x,int y)
{int a=find(x);//x的根节点为a
int b=find(y);//y的根节点为b
if(a!=b)//a,b不是相同根节点,说明ab不连通
{pre[a]=b;//将ab相连,将a的前导结点设为b
}
}
例题:P1195 口袋的天空 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
N个云朵,M个关系,云朵通过代价L连成K棉花糖,一个棉花糖至少需要一个云朵,输出最小代价。
首先让我们选k朵云做棉花糖,换句话说就是选K个节点构造最小生成树。全部n个点的最小生成树有(n-1)条边,那选k个节点就是(n-k)条边。一共就m条边,如果都选完了还没有选出(n-k)条边来,就是No Answer了。
#include<iostream>
#include<algorithm>
using namespace std;
struct woyaohongming{
int s,e,w; //s,w代表边上的两个节点,w权值费用
}a[200005];
int pre[200005]; //并查集用的f数组
bool cmp(woyaohongming a,woyaohongming b){ //sort排序规则,按费用从低到高排序
return a.w<b.w;
}
int find(int a){ //并查集的“找祖宗”函数,路径压缩
if(pre[a]==a)
return a;
else return pre[a]=find(pre[a]);
}
int main(){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
pre[i]=i; //并查集数组初始化,每个节点的祖宗一开始是它自己
for(int i=1;i<=m;i++)
cin>>a[i].s>>a[i].e>>a[i].w; //输入图的信息
sort(a+1,a+1+m,cmp); //快活的按权值排个序
int cnt=0,sum=0; //cnt是已经选中的边数,sum是最终要输出的最小权值
for(int i=1;i<=m;i++){ //m条边,循环m次
if(find(a[i].s)!=find(a[i].e)){ //如果俩节点的祖宗不相等(也就是不是回路),就可以加进去
pre[find(a[i].s)]=find(a[i].e); //把它俩合并成一个祖宗
sum+=a[i].w; //更新最小费用
cnt++; //边数+1
}
if(cnt>=n-k) //边数到达(n-k)条边,任务完成,break
break;
}
if(cnt>=n-k)
cout<<sum; //输出最小权值
else cout<<"No Answer";
return 0;
}
拓扑排序:
在一个有向图中,对所有结点进行排序,要求没有一个节点指向它前面的节点。先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。一直做改操作,直到所有的节点都被分离出来。如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序。
本周看的拓扑排序的内容不算太多,主要看了上课讲的拓扑排序的例题,还是有点不太理解,总之能总结的内容不多。而且,下星期要开始以看题做题了,会多看一下关于此方面的内容,加强对拓扑排序算法的理解和应用。