一.并查集

1.普通的并查集

查找:

int find(int x){
return x==fa[x]?x:find(fa[x]);//如果父节点不是自己,那么去找父节点的父节点
}

合并:

for(int i=1;i<=n;i++)fa[i]=i;//预处理
x=read();y=read();//读入
if(find(x)!=find(y))//如果不是同一个并查集的话才需要合并fa[find(x)]=find(y);//把x作为子节点 合并到y中

2.路径压缩

合并方法相同,查找方法不同;

路径压缩 查找更快了 但是 破坏了树的形状;

复杂度分析参考:数据结构学习 并查集讲解(思路,时间复杂度)

int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);
}

3.启发式合并(按秩合并)

启发式合并 是 高度小的集合 合并到 高度大的集合中,高度max=logn;

启发式合并不能用路径压缩 (因为路径压缩会破坏树的形状);

查找方法同普通的并查集;

合并:

int height[maxn];
void ini(){
for(int i=1;i<=n;i++)fa[i]=i;//在主函数中预处理
}
void merge(int x,int y){x=find(x);y=find(y);if(height[x]>height[y])fa[y]=x;else{fa[x]=y;if(height[x]==height[y])height[y]++;}
}

4.带权值的并查集(路径压缩)

合并与普通的并查集相同;

查找:

int find(int x){if(x==fa[x])return x;int t=fa[x];fa[x]=find(fa[x])v[x]+=v[t];//权值修改return fa[x];
}

5.例题:#4668. 冷战

冷战 - 题目 - 黑暗爆炸OJ (darkbzoj.tk)

(题目之后补全)

二.最小生成树

1.Kruskal法

0.定义 边 的结构体;【注意边的数量最多有n*(n+1)/2条,其中n是端点的数量】

1.首先将所有的边的长度从小到大排序;

2.枚举边,如果边上的两个端点不在同一个并查集里面的话,把这两个端点放到同一个并查集里;

3.因为最小生成树有n-1条边,所以计数 已经 枚举过的 边 的个数,枚举到n-1的时候break;

模板

int fa[N];
struct Edge
{ll u,v,w;bool operator< (const Edge &a)const{return w < a.w;}
}edge[M];
int find(int x){return fa[x]==x?x:fa[x]=fing(fa[x]);
}
ll kruskal(){sort(edges+1,edges+m+1);//m是边的个数,从1存边for (int i=1;i<=n;i++)fa[i]=i;    // 初始化并查集ll res=0,cnt=0;//res权值,cnt生成树边的数量for (int i=1;i<=m;i ++ ){ll a=find(edges[i].u),b=find(edges[i].v),w=edges[i].w;if (a!=b){fa[a]=b;res+=w;cnt++;if(cnt=n-1)break;}}return res;
}

例题

第九届“图灵杯”NEUQ-ACM程序设计竞赛个人赛: L-WireConnection

代码:【注意计算边的长度要开long long!!】(算边的时候dd记得是double类型的)

#include<bits/stdc++.h>
using namespace std;
long long x[2004],y[2004],z[2004];
int fa[2004];
struct node
{int u,v;long long w;
}edge[9000000];
bool cmp(node a,node b){return a.w<b.w;
}
long long ddd(int i,int j)
{double dd=sqrt(1.0*(x[i]-x[j])*(x[i]-x[j])+1.0*(y[i]-y[j])*(y[i]-y[j])+1.0*(z[i]-z[j])*(z[i]-z[j]));return ceil(dd);
}
int find(int x)
{return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{cin.tie(0);int n;cin>>n;for(int i=1;i<=n;i++)cin>>x[i]>>y[i]>>z[i];int cnt=0;for(int i=1;i<=n-1;i++){for(int j=i+1;j<=n;j++){edge[++cnt].u=i;edge[cnt].v=j;edge[cnt].w=ddd(i,j);edge[++cnt].u=j;edge[cnt].v=i;edge[cnt].w=ddd(i,j);}}for(int i=1;i<=n;i++)fa[i]=i;sort(edge+1,edge+1+cnt,cmp);int k=0;long long ans=0;for(int i=1;i<=cnt;i++){int a=find(edge[i].u),b=find(edge[i].v);if(a==b)continue;fa[a]=b;ans+=edge[i].w;k++;if(k==n-1)break;}cout<<ans;return 0;
}

例题:最大生成树 poj1797 heavy transportation

1.排列顺序从大到小;2. 起点1和终点n打通就break;

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m,T;
struct node
{int u,v,w;
}e[500505];
int fa[1004];
bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
}
int main()
{T=read();for(int pp=1;pp<=T;pp++){n=read();m=read();int ans=0x3f3f3f3f;for(int i=1;i<=n;i++)fa[i]=i;for(int i=0;i<m;i++){e[i].u=read();e[i].v=read();e[i].w=read();}sort(e,e+m,cmp);for(int i=0;i<m;i++){int a=find(e[i].u),b=find(e[i].v);if(a==b)continue;fa[a]=b;ans=min(ans,e[i].w);if(find(1)==find(n))break;}cout<<"Scenario #"<<pp<<":\n";cout<<ans<<"\n\n";}return 0;
}

三.最短路

1.Floyd

用来干什么?——求点与点之间的最短路,更新通过k的最短路径;

模板

【注意中转节点一定要放在最外面】(原因如下:)

Floyd算法为什么把k放在最外层?- 知乎

Floyd Warshall Algorithm - general - CodeChef Discuss

for(int k=1;k<=n;k++) //中转节点 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(dis[i][j]>dis[i][k]+dis[k][j])如果直接到达比通过k这个中转接点到达的距离短   dis[i][j]=dis[i][k]+dis[k][j];那么就更新松弛

例题 1 程序设计竞赛月赛-Short Path

​​​​​​acm.ecnu.edu.cn 497

#include<bits/stdc++.h>
using namespace std;
int n;
long long mp[501][501],dis[501][501],ans;
bool vis[501][501];
int main()
{cin>>n;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){cin>>mp[i][j];dis[i][j]=mp[i][j];}for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(k==i||k==j||i==j)continue;if(dis[i][j]>dis[i][k]+dis[k][j]){dis[i][j]=dis[i][k]+dis[k][j];}else if(dis[i][j]==dis[i][k]+dis[k][j]){vis[i][j]=1;vis[j][i]=1;}}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(dis[i][j]<mp[i][j]){printf("-1\n");return 0;}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(!vis[i][j]&&i!=j){ans+=dis[i][j];vis[i][j]=1;vis[j][i]=1;}cout<<ans<<endl;
}

例题2  P1119 灾后重建

P1119 灾后重建 - 洛谷

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long dis[250][250];
bool vis[250];
struct vi{int num;int ok;}v[250];
bool cmp(vi a,vi b){return a.ok<b.ok;}
void update(int k)
{for(int i=0;i<n;i++)for(int j=0;j<n;j++)if(dis[i][j]>dis[i][k]+dis[k][j])dis[i][j]=dis[i][k]+dis[k][j];
}
int main()
{cin>>n>>m;for(int i=0;i<n;i++)for(int j=0;j<=i;j++){dis[i][j]=0x3f3f3f3f;dis[j][i]=0x3f3f3f3f;}for(int i=0;i<n;i++){cin>>v[i].ok;v[i].num=i;}for(int i=0;i<m;i++){int u,v,w;cin>>u>>v>>w;dis[u][v]=w;dis[v][u]=w;}sort(v,v+n,cmp);int q;cin>>q;int nu=0;while(q--){int x,y,t;cin>>x>>y>>t;while(v[nu].ok<=t){if(nu>=n)break;update(v[nu].num);vis[v[nu].num]=1;nu++;}if(dis[x][y]==0x3f3f3f3f||!vis[x]||!vis[y])cout<<-1<<'\n';else cout<<dis[x][y]<<'\n';}return 0;
}

2.Dijkstra算法

用来做什么的?——求出一个点到其他点的最短路径;

算法原理参考:Dijkstra算法详解 - 知乎

为什么dijkstra是正确的:dijkstra 详解 - little_sun 的洛谷博客

0.首先设 要求的 起点为u,总共有n个点,u到所有的点的距离放在mp二维数组里;

1.把点u到其他所有点的路径 存在dis数组里,没有路径就是无穷大,u设置为访问过;

2.找到 u到 剩下没有访问过的点 中  路径最短 的 那个点temp;

3.用dis[temp]更新全部的点,如果dis[temp]+mp[temp][j]<dis[j],那么dis[j]被更新;

4.重复 第二步和第三步 n-1次,这样u到其他点的最短路径就求好了;

未优化版本模板:

【一定要记得没有路径的地方dis要初始化成最大值!!!!】

void dij(int u)
{memset(vis,0,sizeof(vis));for(int i=1;i<=n;i++)dis[i]=mp[u][i];vis[u]=1;dis[u]=0;for(int i=2;i<=n;i++){long long minn=INF;int temp;for(int j=1;j<=n;j++)if(!vis[j]&&dis[j]<minn){temp=j;minn=dis[j];}vis[temp]=1;for(int j=1;j<=n;j++)if(!vis[j]&&dis[temp]+mp[temp][j]<dis[j])dis[j]=dis[temp]+mp[temp][j];}
}

例题1:第十三届西南民族大学程序设计竞赛 B题

留春春不住,春归人寂寞。

代码:

#include<bits/stdc++.h>
using namespace std;
const int INF  = 0x3f3f3f3f;
const int maxn =  1001;
bool vis[maxn];
long long mp[maxn][maxn],dis[maxn];
int n, m;
void dij(int u)
{ for(int i=1;i<=n;i++){dis[i]=mp[u][i];}vis[u]=1;dis[u]=0;for(int i=2;i<=n;i++){long long minn=INF;int temp;for(int j=1;j<=n;j++)if(!vis[j]&&dis[j]<minn){temp=j;minn=dis[j];}vis[temp]=1;for(int j=1;j<=n;j++)if(!vis[j]&&dis[temp]+mp[temp][j]<dis[j])dis[j]=dis[temp]+mp[temp][j];}
}
int main()
{cin.tie(0);long long i, j, x, y, w;cin>>n>>m;memset(mp,0x3f,sizeof(mp));while(m--){cin>>x>>y>>w;mp[x][y] = w;mp[y][x] = w;}dij(1);int q;cin>>q;for(int i=0;i<q;i++){cin>>x;cout<<dis[x]<<"\n";}return 0;
}

例题2:poj1797 Heavy Transportation

题意:从1走到n,选一条路径,该路径上的边权最大;

思路:

1.找到一个 与起点相连 载重量最大的边 的 端点temp,考虑1到 j点 经过temp;

【前提是temp到j之间有路】

2.如果 起点 到 j点 的载重量比 min(dis[temp],mp[temp][j]) 这条路小;

那么就更新成 min(dis[temp],mp[temp][j]) ,代表从1到 j点 走1-temp-j 的路径;

否则放在那里不动,代表代表从1到 j点 仍然走1 -j 这条路;

因此该题是dijkstra的变形,求dis[j]=max(dis[j],min(dis[temp],mp[temp][j]));

代码:

#include<iostream>
#include<string.h>
#include<cstdio>
using namespace std;
int n,m,T;
int dis[1005],vis[1005],mp[1005][1005];
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
}
void dij(int u)
{for(int i=1;i<=n;i++){dis[i]=mp[u][i];}vis[u]=1;dis[u]=0;for(int i=2;i<=n;i++){int maxx=0,temp;for(int j=1;j<=n;j++)if(!vis[j]&&dis[j]>maxx){temp=j;maxx=dis[j];}vis[temp]=1;for(int j=1;j<=n;j++)if(!vis[j]&&mp[temp][j]>0){dis[j]=max(dis[j],min(dis[temp],mp[temp][j]));}}
}
int main()
{T=read();for(int pp=1;pp<=T;pp++){memset(mp,-1,sizeof(mp));memset(vis,0,sizeof(vis));n=read();m=read();for(int i=1;i<=m;i++){int u,v,w;u=read();v=read();w=read();mp[u][v]=w;mp[v][u]=w;}dij(1);cout<<"Scenario #"<<pp<<":\n";cout<<dis[n]<<"\n\n";}return 0;
}

堆优化版本模板:

【注意⚠:dis一定要无穷!!!!两个地方的vis还是要有!!!更新完一个,放堆里一个】

【特别是if (vis[t.second])continue; 一定要有】

void dij(int u)
{priority_queue<int,vector<pair<long long,int> >,greater<pair<long long,int> > > heap;memset(dis,0x3f,sizeof(dis));heap.push({0,u});dis[u]=0;while(!heap.empty()){pair<long long,int> t;t=heap.top();heap.pop();//找到最小的if (vis[t.second])continue;//注意这里vis有,否则TLEvis[t.second]=1;for(auto i:mp[t.second]){     //相当于dis[j]>dis[temp]+mp[temp][j]if(!vis[i.second]&&dis[i.second]>dis[t.second]+i.first){dis[i.second]=dis[t.second]+i.first;heap.push({dis[i.second],i.second});}}}
}

例题:P4779 【模板】单源最短路径(标准版)

代码:

【注意是单向边!!!】

#include<bits/stdc++.h>
using namespace std;
int n,m,s;
bool vis[100005];long long dis[100005];
vector< pair<long long,int> >mp[200005];
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
}
void dij(int u)
{priority_queue<int ,vector< pair<long long,int> >,greater< pair<long long,int> > > heap;memset(dis,0x3f,sizeof(dis));heap.push({0,u});dis[u]=0;while(!heap.empty()){pair<long long,int> t;t=heap.top();heap.pop();//找到最小的if (vis[t.second]) continue;vis[t.second]=1;for(auto i:mp[t.second]){//dis[j]>dis[temp]+mp[temp][j]if(!vis[i.second]&&dis[i.second]>dis[t.second]+i.first){dis[i.second]=dis[t.second]+i.first;heap.push({dis[i.second],i.second});}}}
}
int main()
{n=read();m=read();s=read();for(int i=0;i<m;i++){int u,v,w;u=read();v=read();w=read();mp[u].push_back({w,v});}dij(s);for(int i=1;i<=n;i++)cout<<dis[i]<<" ";return 0;
}

线段树优化模板(不会)

【模板】单源最短路径(标准版)】 - GKxx 的博客 - 洛谷博客 (luogu.com.cn)

3.SPFA算法

并查集+最小生成树(Kruskal)+最短路(Floyd、Dijkstra)相关推荐

  1. 基于并查集的kruskal算法

    #include <iostream> //并查集的kruskal算法using namespace std;const int max_ve=1005,max_ed=15005;int ...

  2. Kruskal算法:贪心+并查集=最小生成树

    http://www.51nod.com/ Kruskal算法的高效实现需要一种称作并查集的结构.我们在这里不介绍并查集,只介绍Kruskal算法的基本思想和证明,实现留在以后讨论. Kruskal算 ...

  3. 计科练习12题解(并查集,最小生成树)

    目录 黑暗意志 闪闪发光 最小生成树(Prim) 并查集 隔离 最小生成树(Kruskal) 黑暗意志 题目描述 在数千年前潘达利亚从卡利姆多分离之时,迷雾笼罩着这块新形成的大陆,使它不被外来者发现. ...

  4. 【HDU - 1863】 畅通工程(并查集+最小生成树)

    题干: 省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).经过调查评估,得到的统计表中列出了有可能建设公路的 ...

  5. 20211229[按秩合并并查集 最小生成树][BZOJ4668]冷战

    20211229[按秩合并并查集.最小生成树][BZOJ4668]冷战 题意:给定N点,动态加边与询问两点最早是哪条边开始连通,强制在线 首先如果离线的话可以直接跑最小生成树,不过代码不好处理. 当然 ...

  6. 畅通工程 hdu 1232 HDU - 1863 (并查集+最小生成树)

    畅通工程hdu 1232 并查集 Problem Description Input Output 参考代码 HDU - 1863 Problem Description Input Output 参 ...

  7. PIPI OJ 1118: 继续畅通工程(并查集+最小生成树)

    菜鸟生成记(18) 1118: 继续畅通工程 又双叒叕是最短路径的水题;不同的是,在构造最小生成树前,题目中已经规定一些已经建好了(这些边已经在生成树里面了);从未建好的边中选择最优边加入生成树;直到 ...

  8. [并查集][最小生成树]PJOJ 2560 雀斑连线/luogu 2921 修复公路

    题目描述 In an episode of the Dick Van Dyke show, little Richie connects the freckles on his Dad's back ...

  9. 图论 + 并查集 ----最小生成树重构图 + 可撤销并查集 + set启发式合并 时间线上的离线求解 D. Graph and Queries

    解题思路 题目大意: 就是给你一个无向图,每个点都有一个权值,和qqq次询问 每次询问有两种操作 1 x:就询问从x点出发,能访问到的最大权值是多少,并把最大权值那个点的权值设置为0 2 x:就是删除 ...

  10. 牛客-Forsaken喜欢独一无二的树【并查集,最小生成树】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/1221/H 题目大意 给一张图,要求删掉一些边使得最小生成树权值不变,然后求删掉的边的最小权值. 解题思路 我 ...

最新文章

  1. SpringBoot文件上传异常之temporary upload location not valid
  2. 【Cocosd2d实例教程七】Cocos2d实现超炫的粒子效果!!
  3. jQuery的JSONP
  4. F4 value help and HANA native SQL
  5. Codeforces Round #704 (Div. 2) D. Genius‘s Gambit 构造 + 细节
  6. 阿里内部资料!如何试出一个Android开发者真正的水平?系列教学
  7. 与smart_近视激光手术之smart篇
  8. QQ小工具网页版源码
  9. shiro 解决 跨域(仅端口不同) 登陆 问题
  10. 我这几年来是如何编写 Go HTTP 服务的(转载)
  11. react-native踩坑的开始!
  12. Python核心编程答案(自整理)
  13. Vue.js 与 Webpack externals 的使用
  14. Could not find a declaration file for module 'vue-xxx'.
  15. cacti 安装部署
  16. C# Winform 自动更新程序实例
  17. 互动拍照 — 子弹时间
  18. 第十七部分 Istio控制 Egress 流量
  19. docker 创建 Carte 服务
  20. 2018初中计算机考试知识点,2018计算机等级考试考点:考前学习的技巧

热门文章

  1. App主界面交互框架(其中包括标签式、跳板式、列表式、旋转木马、抽屉式、点聚式、陈列馆式、瀑布流)
  2. PostgreSQL 磁盘空间清理
  3. 微信公共号token验证失败解决办法之一
  4. Frasterrcnn-tensorflow-python3.5-master生成预测坐标位置并存储到xlsl表格中,并生成pr曲线
  5. 聚集索引与非聚集索引的区别
  6. 火箭是这样连胜的(转载)
  7. 皮克定理(计算多边形面积)
  8. veeam的备份策略(每周6增量备份,每周日合成全量备份)
  9. Java精简高效异步编程实战
  10. 2021-07-10蓝桥杯单片机学习知识点总结