kruskal算法 生成最小生成树:
算法思想: 将边权按由小到大排序, 每次将权重最小的union, 并且记录其权重, 边界条件:
- 如果有公共的根节点,则说明成环了 不可,2. 如果并入的边 == 节点个数-1 , 则为最小生成树了,退出循环
1.还是畅通工程_牛客题霸_牛客网 利用并查集和kruskal算法
cpp
using namespace std;
const int N = 10010;
int father[N];
int high[N];
void InitTree(int n){
for(int i = 1;i<=n;i++){
father[i] = i;
high[i] = 0;
}
}
int FindTree(int u){
if(u==father[u])
return u;
else{
father[u] = FindTree(father[u]);
return father[u];
}
}
void UnionTree(int u,int v){
int uroot = FindTree(u);
int vroot = FindTree(v);
if(uroot!=vroot){
if(high[uroot]<high[vroot])
father[uroot] = vroot;
else if(high[uroot]>high[vroot])
father[vroot] = uroot;
else{
father[vroot] = uroot;
high[uroot]++;
}
}
}
struct Edge{
int u;
int v;
int w;
Edge(int _u,int _v,int _w){//内置构造函数, 函数名字和结构体名字一致,类内没有返回值
u = _u;
v = _v;
w = _w;
}
};
bool compare(Edge lsh,Edge rsh){
return lsh.w<rsh.w;//结构体不知道如何比较大小,需要内置
}
int main(){
int n;
while(cin>>n){//
if(n==0)
break;
vector<Edge> edgeVec;//边数组
InitTree(n+1);//初始化为0-n个节点的树
for(int i = 0;i<n*(n-1)/2;i++){
int u,v,w;
cin>>u>>v>>w;
Edge e(u,v,w);//Edge 变量 ,变量名e
edgeVec.push_back(e); //边压入
}
//kruskal排序 权重排序 按照边权值由小到大
sort(edgeVec.begin(),edgeVec.end(),compare);
int totalweight = 0;//边权值
int curnum = 0;//目前加入的边数
for(int i = 0;i<edgeVec.size();i++){//遍历边数组
int u = edgeVec[i].u;
int v = edgeVec[i].v;
int w = edgeVec[i].w;
if(FindTree(u)!=FindTree(v)){//不是同一个集合
UnionTree(u,v);//合并
totalweight+=w;//增加权重
curnum++; //增加子图的边
if(curnum == n-1)//退出边界条件
break;
}
}
cout<<totalweight<<endl;
}
return 0;
怎么样处理已经连接过的路径呢?
利用两次遍历循环,先遍历边数组结构 1.把t==1 的边先合并 2. 在构建一个unbuild数组 ,把t==0的数组合并 。将其排序, 利用kruskal算法,求其权重
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
int father[N];
int high[N];
void InitTree(int n ){
for(int i =1;i<=n;i++){
father[i] = i;
high[i] = 0;
}
}
int FindTree(int u){
if(u==father[u])
return u;
else {
father[u] = FindTree(father[u]);
return father[u];
}
}
void UnionTree(int u,int v){
int uroot = FindTree(u);
int vroot = FindTree(v);
if(uroot != vroot){
if(high[uroot]>high[vroot])
father[vroot] = uroot;
else if(high[uroot]<high[vroot])
father[uroot] = vroot;
else{
father[vroot] = uroot;
high[uroot] ++;
}
}
}
struct Edge{
int u,v,w,t;
Edge(int _u,int _v,int _w,int _t){
u = _u;
v = _v;
w = _w;
t = _t;
}
};
bool compare(Edge lsh,Edge rsh){
return lsh.w<rsh.w;
}
int main(){
int n;
while(cin>>n){
if(n==0)
break;
InitTree(n);
vector<Edge> vecEdge;
for(int i = 0;i<n*(n-1)/2;i++){
int a,b,c,d;
cin>>a>>b>>c>>d;
Edge e(a,b,c,d);
vecEdge.push_back(e);
}
//先将t==1的合并
for(auto &e:vecEdge){
if(e.t ==1)
UnionTree(e.u,e.v);
}
//构建没有合并的节点数组
vector<Edge> unbuild;
for(auto &e:vecEdge){
if(e.t == 0)
unbuild.push_back(e);//压入数组
}
//利用kruskal算法,排序
sort(unbuild.begin(),unbuild.end(),compare);
int totalweight = 0;
int curnum = 0;
for(auto &e:unbuild){//遍历unbuild,增加其权值
if(FindTree(e.u)!= FindTree(e.v)){
UnionTree(e.u,e.v);
totalweight += e.w;
}
}
if(curnum == n-1)
break;
cout<<totalweight<<endl;
}
return 0;
}
题目意思: 输入n个点的坐标 ,求生成最小子树的最短路径,我们手动求解dist
dist[vec[i],vec[j]] 传入的是整个point节点
思路:整体思路 并查集加kruskal算法
细节:
定义point 结构体存放 坐标
定义Edge 结构体 存放 遍历的节点去向, from to 以及权值
用函数dist () 计算距离
cpp
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int father[N];
int high[N];
void InitTree(int n){
for(int i = 1;i<=n;i++){
father[i] = i;
high[i] = 0;
}
}
int FindTree(int u){
if(u == father[u])
return u;
else{
father[u] = FindTree(father[u]);
return father[u];
}
}
void UnionTree(int u,int v){
int uroot = FindTree(u);
int vroot = FindTree(v);
if(uroot!=vroot){
if(high[uroot]>high[vroot])
father[vroot] = uroot;
else if(high[uroot] < high[vroot])
father[uroot] = vroot;
else{
father[vroot] = uroot;
high[uroot]++;
}
}
}
struct Point{
float x,y;
Point (float a, float b){
x = a;
y = b;
} //内嵌函数
};
struct Edge{
float from,to,w;
Edge(float _f,float _t , float _w){
from =_f;
to = _t;
w = _w;
}//内嵌函数
};
bool compare(Edge x,Edge y){
return x.w<y.w;
}
float dist(Point x, Point y){
return pow((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y),0.5);//计算距离
}
int main(){
int n;
while(cin>>n){
InitTree(n);
vector<Point> vec;
for(int i =0;i<n;i++){
float a,b;
cin>>a>>b;
Point p(a,b);
vec.push_back(p);//压入坐标
}
vector<Edge> graph;
for(int i = 0;i<vec.size();i++){
for(int j = i+1;j<vec.size();j++){
graph.emplace_back(i+1,j+1,dist(vec[i],vec[j])); //压入图中, 遍历点与点之间的距离
}
}
sort(graph.begin(),graph.end(),compare);//排序由小到大
double res= 0.0;
for(auto &e: graph){//遍历
if(FindTree(e.from)!= FindTree(e.to)){//判断不为公共节点
UnionTree(e.from,e.to);
res+=e.w;
}
}
printf("%.2f\n",res);
}
return 0;
}
j