电子学会七级数据结构-最小生成树
电子学会七级数据结构-最小生成树
P2330 [SCOI2005]繁忙的都市
https://www.luogu.com.cn/problem/P2330
kruskal
#include<bits/stdc++.h>
using namespace std;
const int maxn=310;
const int maxm=1e5+10;
struct edge{//边集数组
int x,y,w;//起点 终点 长度
}e[maxm];
int n,m;
int f[maxn];//i点父节点编号
//从x找根 路径压缩算法 不是根 把自己挂在根下面
int find(int x){
if(f[x]!=x){// 父节点不是自己 即不是根
f[x]=find(f[x]);//通过父节点找根 并把根赋值给x的父节点 x加入根节点子节点 路径压缩
}
return f[x];
}
bool cmp(edge P,edge Q){
return P.w<Q.w;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);//m条道路 起始点 终点 边权
}
sort(e+1,e+m+1,cmp);//按边权从小到大排序
for(int i=1;i<=n;i++){
f[i]=i;//i的父节点设置自己 所有节点初始为根节点
}
int cnt=0;
for(int i=1;i<=m;i++){//遍历m条边
int rx=find(e[i].x);//找x根节点
int ry=find(e[i].y);//找y的根节点
if(rx!=ry){//根不同 不连通 可以加入连通
if(++cnt==n-1){
printf("%d %d\n",n-1,e[i].w);//分值最大的道路分值 最后的一条 已排序
return 0;
}
f[rx]=ry;//连通 fx的父节点指向ry rx和ry有一个根节点 连通
}
}
}
【模板】最小生成树
https://www.luogu.com.cn/problem/P3366
kruskal
#include<bits/stdc++.h>
using namespace std;
const int maxn=5010;
const int maxm=2e5+10;
struct edge{//边集数组
int x,y,w;//起点 终点 长度
}e[maxm];
int n,m,ans;
int f[maxn];//i点父节点编号
//从x找根 路径压缩算法 不是根 把自己挂在根下面
int find(int x){
if(f[x]!=x){// 父节点不是自己 即不是根
f[x]=find(f[x]);//通过父节点找根 并把根赋值给x的父节点 x加入根节点子节点 路径压缩
}
return f[x];
}
bool cmp(edge P,edge Q){
return P.w<Q.w;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);//m条道路 起始点 终点 边权
}
sort(e+1,e+m+1,cmp);//按边权从小到大排序
for(int i=1;i<=n;i++){
f[i]=i;//i的父节点设置自己 所有节点初始为根节点
}
int cnt=0;
for(int i=1;i<=m;i++){//遍历m条边
int rx=find(e[i].x);//找x根节点
int ry=find(e[i].y);//找y的根节点
if(rx!=ry){//根不同 不连通 可以加入连通
f[rx]=ry;//连通 fx的父节点指向ry rx和ry有一个根节点 连通
ans+=e[i].w;
if(++cnt==n-1){//合并一次cnt+1 n个点 总共集合合并n-1次
break;
}
}
}
if(cnt==n-1){//如果不是n-1说明有点不连通
cout<<ans;
}else{
cout<<"orz";
}
}
prim
#include<bits/stdc++.h>
using namespace std;
const int N = 5000 + 10, M = 2e5 + 10, INF = 0x3f3f3f3f;
struct Edge{
int v,w,nxt;
}e[M<<1];
int hd[N],dis[N],vis[N],ecnt;
int n,m,ans;
void add(int u, int v, int w){//链式前向星加边
e[++ecnt].v=v;
e[ecnt].w=w;
e[ecnt].nxt=hd[u];
hd[u]=ecnt;
}
int prim(){
memset(dis, 0x3f, sizeof(dis));
dis[1]=0;//初始起点dis[1]=0 其余为正无穷
int res=0;
for(int i=0;i<n;i++){
int t=0;//最大值下标 0下标dis正常边不使用
for(int j=1;j<=n;j++){//v-s集合任意点找到v集合最小距离
if(vis[j]) continue;//j被标记为v集合 不再处理
if(dis[t]>dis[j])//找到最小的dis 记录下标
t=j;
}
if(dis[t]==INF) return INF;//如果找到最小的dis[t]是默认最大值 则不连通
res += dis[t];//dis加入v 找到一条边
vis[t] = 1;//t加入v集合
for(int ei=hd[t];ei;ei=e[ei].nxt){//松弛连接点
int v=e[ei].v,w=e[ei].w;
if(vis[v]==0&&dis[v]>w){//t的这几个邻接点 是否可以通过t变小
dis[v] = w;
}
}
}
return res;
}
int main(){
scanf("%d %d",&n,&m);//输入n个点 m条边
for(int i=1;i<=m;i++) {//输入m条边
int u,v,w;
scanf("%d %d %d",&u,&v,&w);//输入起点 终点 边权
add(u,v,w);
add(v,u,w);//加边 无向边
}
ans = prim();
if(ans == INF)
printf("orz");
else
printf("%d", ans);
return 0;
}
prim 优先队列优化
#include<bits/stdc++.h>
using namespace std;
const int N=5000+10,M=2e5+10,INF=0x3f3f3f3f;
struct Edge{
int v,w,nxt;//终点 边权 下一个 链式前向星
}e[M<<1];
//hd[N]链式前向星 头节点 可以根据这个元素找邻接点
//dis[N]当前节点到已加入最最小生成树集合的最短距离
//vis[N]当前节点是否已加入最短路集合
//ecnt维护链式前向星边数 每增加一条边加1
int hd[N],dis[N],vis[N],ecnt;
int n,m,ans,tcnt;
//链式前向星加边
//u 起点 v终点 w边权
/*
1-->2 w=5
1-->3 w=9
1-->4 w=7
加边1-->2
e[1].v=2
e[1].w=5
e[1].nxt=0
hd[1]=1
加边1-->3
e[2].v=3
e[2].w=9
e[2].nxt=1
hd[1]=2
加边1-->4
e[3].v=4
e[3].w=7
e[3].nxt=2
hd[1]=3
如果找1节点的邻接点,可以如下操作
通过h[1]找到其中一个邻接点3
通过e[3].nxt找到另外一个邻接点2
通过e[2].nxt找到另外一个邻接点1
三个邻接点都找到了,此时 e[1].nxt=0 找邻接点结束
*/
void add(int u, int v, int w){
e[++ecnt].v=v;//++ecnt增加一条边 数组下标从1开始
e[ecnt].w=w;//边权赋值
e[ecnt].nxt=hd[u];//下一个节点指向上一个hd[u](此时的hd[u]为0或者上一个邻接点)
hd[u]=ecnt;//hd[u]指向此邻接点下标
}
typedef pair <int,int> pii;//一种模板类型 可以包括两个数据类型 first second
priority_queue <pii,vector<pii>,greater<pii> > q;//通过pair构造小顶堆
void prim(){
memset(dis, 0x3f, sizeof(dis));//dis初始每个最大值
dis[1]=0;//从第一个节点加入最小生成树 1-->1集合最小距离为0 其他节点也可以
q.push(pii(0,1));//first 到v集合最小距离 second 到v集合最小距离节点编号
while(!q.empty()&&tcnt<n){
int d = q.top().first, u=q.top().second;
q.pop();
if(vis[u]) continue;//如果已在v集合 不在放入v集合
tcnt++;//v集合节点树+1
ans+=d;//边权累加到ans
vis[u]=1;//标识 u已经放入v集合
for(int i=hd[u];i;i=e[i].nxt){//松弛u的邻接点
int v=e[i].v,w=e[i].w;
//如果v未接入v-(已确定最小生成树的集合)集合
//并且到v集合的距离可以更短
if(vis[v]==0&&dis[v]>w){
dis[v]=w;//用更短的更新dis[v]
q.push(pii(dis[v],v));//把v节点放入优先队列 进入下一次找最小 松弛邻接点
}
}
}
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
add(u,v,w);//加边 比如 1-->2
add(v,u,w);//加边 无向图 比如 2-->1
}
prim();//prim算法求最小生成树
if(tcnt < n)//找到最小生成树节点数tcnt 比总节点数n小 说明有节点没连通
printf("orz");
else
printf("%d",ans);
return 0;
}
[USACO07DEC]Building Roads S
https://www.luogu.com.cn/problem/P2872
无线通讯网
https://www.luogu.com.cn/problem/P1991
营救
https://www.luogu.com.cn/problem/P1396
拆地毯
https://www.luogu.com.cn/problem/P2121
买礼物
https://www.luogu.com.cn/problem/P1194
口袋的天空
https://www.luogu.com.cn/problem/P1195
[JSOI2010]部落划分
https://www.luogu.com.cn/problem/P4047
[NOIP2013 提高组] 货车运输
https://www.luogu.com.cn/problem/P1967
作者:newcode 更多资源请关注纽扣编程微信公众号
从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习