电子学会七级数据结构-最小生成树

电子学会七级数据结构-最小生成树
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

posted @ 2022-05-08 22:18  new-code  阅读(19)  评论(0编辑  收藏  举报