介绍
最小生成树一共两种算法prim算法以及kruskal算法,这两种算法最大的区别在于,一个是对点进行维护,一个是对边权进行维护,感觉kruskal算法相对方便一些
Prim算法
Prim算法类似于最短路的算法,每次找出距离上个点的距离最小的点,然后维护一个记录最小距离的数组
基础代码模板
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
const int inf = 0x3f3f3f3f;
int n,m,mindis[maxn],ans;
bool vis[maxn];
struct edge{
int v,w;
edge(int vv,int ww):v(vv),w(ww){}
};
vector<edge>e[maxn];
void add_edge(int u,int v,int w){
e[u].push_back(edge(v,w));
}
void prim(){
memset(mindis,inf,sizeof(mindis));
memset(vis,false,sizeof(vis));
mindis[1] = 0;//默认从1开始算
for(int i = 1 ; i <= n ; i++){
int minn = inf,u = -1;
for(int j = 1 ; j <= n ; j++){
if(!vis[j] && minn > mindis[j]){
minn = mindis[j];
u = j;
}
}
if(u == -1) break;
vis[u] = true;
for(int j = 0 ; j < e[u].size() ; j++){
int v = e[u][j].v,w = e[u][j].w;
if(!vis[v] && w < mindis[v]){
mindis[v] = w;
}
}
}
}
int main(){
// cin >> n >> m ;
// for(int i = 0 ; i < m ; i++){
// int u,v,w;
// cin >> u >> v >> w;
// add_edge(u,v,w);
// }
cin >> n;
for(int i = 1 ; i <= n ; i++){
for(int j = 1; j <= n ; j++){
int w;
cin >> w;
add_edge(i,j,w);
}
}
prim();
for(int i = 1 ; i <= n ; i++) ans += mindis[i];
cout << ans << endl;
return 0;
}
Kruskal算法
Kruskal算法是对边进行维护,根据边权从小到大排序,利用并查集将在一个连通块中的点的父节点找到,第一次连通的点,边权一定最小,这样遍历所有的点,最后得到的答案就是最小生成树
基础代码模板
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+5;
int pre[maxn],ans,n;
struct edge{
int u,v,w;
edge(int uu,int vv,int ww):u(uu),v(vv),w(ww){}
friend bool operator <(edge a,edge b){
return a.w < b.w;
}
};
vector<edge>e;
void add_edge(int u,int v,int w){
e.push_back(edge(u,v,w));
}
int find(int x){
return x == pre[x] ? x : pre[x] = find(pre[x]);
}
int main(){
int n;
cin >> n;
for(int i = 1 ; i <= n ; i++) pre[i] = i;
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= n ; j++){
int w;
cin >> w;
add_edge(i,j,w);
}
}
sort(e.begin(),e.end());
for(int i = 0 ; i < e.size() ;i++){
int r1 = find(e[i].u),r2 = find(e[i].v);
if(r1 != r2){
pre[r1] = r2;
ans += e[i].w;
}
}
cout << ans << endl;
return 0;
}
Kruskal的应用:
题目背景
“咚咚咚……”“查水表!”原来是查水表来了,现在哪里找这么热心上门的查表员啊!小明感动得热泪盈眶,开起了门……
题目描述
妈妈下班回家,街坊邻居说小明被一群陌生人强行押上了警车!妈妈丰富的经验告诉她小明被带到了 t 区,而自己在 s 区。
该市有 m 条大道连接 n 个区,一条大道将两个区相连接,每个大道有一个拥挤度。小明的妈妈虽然很着急,但是不愿意拥挤的人潮冲乱了她优雅的步伐。所以请你帮她规划一条从 s 至 t 的路线,使得经过道路的拥挤度最大值最小。
输入格式
第一行有四个用空格隔开的 n,m,s,t,其含义见【题目描述】。
接下来 m 行,每行三个整数 u,v,w,表示有一条大道连接区 u 和区 v,且拥挤度为w。
两个区之间可能存在多条大道。
输出格式
输出一行一个整数,代表最大的拥挤度。
输入输出样例
输入 #1
3 3 1 3
1 2 2
2 3 1
1 3 3
输出 #1
2
样例输入输出 1 解释
小明的妈妈要从 111 号点去 333 号点,最优路线为 111->222->333
题解:
这题问的是拥挤度最大值的最小值,开始题目意思理解错了,以为是最短路,后来发现意思是,找到从s到t过程中经历的路中的最大值,然后让这个最大值最小。直接用克鲁斯卡尔跑一遍,如果第一次找到s和t连通就输出这个边的权值,因为克鲁斯卡尔是需要对边权从小到大排序的,那么一旦连通,就说明在这个连通块中,最后那个边的值是最大的,又因为前面排序过了,所以这个值必是最小的
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int inf = 0x3f3f3f3f;
int pre[maxn];
int n,m,s,t,cnt;
struct node{
int u,v,w;
}e[maxn];
bool cmp(node a,node b){
return a.w < b.w;
}
int find(int x){
return x == pre[x] ? x :pre[x] = find(pre[x]);
}
int main(){
cin >> n >> m >> s >> t;
for(int i = 1 ; i <= n ; i++) pre[i] = i;
for(int i = 0 ; i < m ; i++){
cin >> e[i].u >> e[i].v >> e[i].w;
}
sort(e,e+m,cmp);
for(int i = 0 ; i < m ; i++){
int r1 = find(e[i].u),r2 = find(e[i].v);
if(r1 != r2) pre[r1] = r2;
if(find(s) == find(t)){
cout << e[i].w << endl;
return 0;
}
}
return 0;
}
- 本文链接:http://yoursite.com/2020/08/05/%E6%9C%80%E5%B0%8F%E7%94%9F%E6%88%90%E6%A0%91%E7%9A%84%E4%B8%A4%E7%A7%8D%E7%AE%97%E6%B3%95/
- 版权声明:本博客所有文章除特别声明外,均默认采用 许可协议。
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub IssuesGitHub Discussions