zoukankan      html  css  js  c++  java
  • 刷题笔记-图-统计连通分量

    统计连通分量思路

    统计连通分量的个数及其他属性(如:总点权,总边权等),思路有三种:DFS、BFS、并查集

    DFS/BFS统计连通分量思路

    DFS/BFS遍历图中所有顶点,DFS/BFS函数调用的次数即为连通分量个数,每次调用DFS/BFS函数会完全遍历一个连通分量的所有顶点(在遍历过程中可以统计其点权和)

    并查集统计连通分量思路

    1 录入边信息时,将边的两个顶点进行并查集操作合并到一个集合中 (一个集合表示一个连通子图),根节点为并查集中每个集合的唯一标识
    2 录入完成后,遍历father[n]数组,将根节点相同的顶点统计到一个连通子图,并求所有连通子图的属性

    统计连通分量总边权注意事项

    计算每个连通分量的总边权时,如果不做处理,会重复计算每条边的边权,每个连通分量边权和统计结果为实际边权总和的两倍

    如何解决边权重复计算的问题?

    思路1

    标记已经访问过的顶点,并且标记已经访问过的边,如此才能保证所有边都被遍历统计而且不被重复统计,最终统计的总边权和为实际总边权和

    标记访问过的边,也有两种思路:1. 访问过边后将该边边权置为0(g[a][b]=g[b][a]=0)2. 访问过边后将其标记为已被访问(evis[a][b]=evis[b][a]=0)

    思路2

    DFS/BFS标记已经访问过的顶点,但顶点中要记录与该顶点直连的所有边的边权,最终统计的总边权和为实际总边权和的两倍(因为每条边的两个顶点都记录了该边的边权)

    深度优先遍历(DFS)

    DFS统计连通分量属性(邻接矩阵)

    #include <iostream>
    using namespace std;
    const int maxn = 1000;
    int n,k,wt[maxn],g[maxn][maxn],vis[maxn];//wt点权,g边权
    void dfs(int i,int &head,int &num,int &tv) {
    	num++;
    	vis[i]=true;
    	if(wt[i]>wt[head])head=i;
    	for(int j=1; j<=n; j++) {
    		if(g[i][j]==0)continue;
    		tv+=g[i][j];
    		g[i][j]=g[j][i]=0; //访问后将边权置为0,防止重复统计 
    		if(vis[j]==false)
    			dfs(j,head,num,tv);
    	}
    }
    void dfs_travel() {
    	int cct=0; //统计连通分量个数 
    	for(int i=1; i<=n; i++) {
    		if(vis[i]==true)continue;
    		int head=i,num=0,tv=0; //head 记录最大点权顶点;num记录顶点数;tv记录总边权
    		dfs(i,head,num,tv);
    		cct++; 
    		/*
    			在此处理各个连通分量
    		*/
    	}
    }
    int main(int argc,char * argv[]) {
    	int w,a,b;
    	scanf("%d %d",&n,&k);
    	for(int i=0; i<n; i++) {
    		scanf("%d %d %d",&a,&b,&w);// 录入边描述:顶点1,顶点2,权重
    		wt[a]+=w; //将边权记录到顶点
    		wt[b]+=w;
    		g[a][b]=w; //记录边权
    		g[b][a]=w;
    	}
    	dfs_travel(); //深度优先遍历
    	return 0;
    }
    

    DFS统计连通分量属性(邻接表)

    #include <iostream>
    #include <vector>
    using namespace std;
    const int maxn = 1000;
    int n,k,wt[maxn],vis[maxn],evis[maxn][maxn];//wt点权,g边权
    struct node {
    	int v;
    	int w; //边权
    };
    vector<node> g[maxn];
    void dfs(int i,int &head,int &num,int &tv) {
    	num++;
    	vis[i]=1;
    	if(wt[i]>wt[head])head=i;
    	for(int j=0; j<=g[i].size(); j++) {
    		if(evis[i][j]==1)continue;
    		tv+=g[i][j].w;
    		evis[i][j]=evis[j][i]=1;
    		dfs(j,head,num,tv);
    	}
    }
    void dfs_travel() {
    	int cct=0; //统计连通分量个数
    	for(int i=1; i<=n; i++) {
    		if(vis[i]==1)continue;
    		int head=i,num=0,tv=0; //head 记录最大点权顶点;num记录顶点数;tv记录总边权
    		dfs(i,head,num,tv);
    		cct++;
    		/*
    			在此处理各个连通分量
    		*/
    	}
    }
    int main(int argc,char * argv[]) {
    	int n,w,a,b;
    	scanf("%d %d",&n,&k);
    	for(int i=0; i<n; i++) {
    		scanf("%d %d %d",&a,&b,&w);// 录入边描述:顶点1,顶点2,权重
    		wt[a]+=w; //将边权记录到顶点
    		wt[b]+=w;
    		g[a].push_back({b,w}); //记录边权
    		g[b].push_back({a,w});
    	}
    	dfs_travel(); //深度优先遍历
    	return 0;
    }
    

    广度优先遍历(BFS)

    BFS统计连通分量属性(邻接矩阵)

    #include <iostream>
    #include <map>
    #include <queue>
    const int maxn=2010;
    using namespace std;
    map<string,int> si;
    map<int,string> is;
    map<string,int> ans;
    int n,k,wt[maxn],g[maxn][maxn],vis[maxn],evis[maxn][maxn];//wt点权,g边权
    void bfs(int now, int &head, int &num, int &tv) {
    	queue<int> q;
    	q.push(now);
    	vis[now]=true;
    	while(!q.empty()) {
    		int i = q.front();
    		q.pop();
    		num++;
    		if(wt[i]>wt[head])head=i;
    		for(int j=1; j<=n; j++) {
    			if(evis[i][j]==1||g[i][j]==0)continue;
    			tv+=g[i][j];
    			evis[i][j]=evis[j][i]=1; //边标记为已被访问 
    			if(vis[j]==false) {
    				q.push(j);
    				vis[j]=true; //顶点标记为已被访问 
    			}
    		}
    	}
    }
    void bfs_travel() {
    	int cct=0; //统计连通分量个数 
    	for(int i=1; i<=n; i++) {
    		if(vis[i]==true)continue;
    		int head=i,num=0,tv=0; //head 记录最大点权顶点;num记录顶点数;tv记录总边权
    		bfs(i,head,num,tv);
    		cct++; 
    		/*
    			在此处理各个连通分量
    		*/
    	}
    }
    int main(int argc,char * argv[]) {
    	int w,a,b;
    	scanf("%d %d",&n,&k);
    	for(int i=0; i<n; i++) {
    		scanf("%d %d %d",&a,&b,&w);// 录入边描述:顶点1,顶点2,权重
    		wt[a]+=w; //将边权记录到顶点
    		wt[b]+=w;
    		g[a][b]=w; //记录边权
    		g[b][a]=w;
    	}
    	bfs_travel(); //深度优先遍历
    	return 0;
    }
    

    BFS统计连通分量属性(邻接表)

    #include <iostream>
    #include <map>
    #include <queue>
    const int maxn=2010;
    using namespace std;
    map<string,int> si;
    map<int,string> is;
    map<string,int> ans;
    int n,k,wt[maxn],vis[maxn],evis[maxn][maxn];//wt点权,g边权
    struct node {
    	int v;
    	int w;
    };
    vector<node> g[maxn];
    void bfs(int now, int &head, int &num, int &tv) {
    	queue<int> q;
    	q.push(now);
    	vis[now]=true;
    	while(!q.empty()) {
    		int i = q.front();
    		q.pop();
    		num++;
    		if(wt[i]>wt[head])head=i;
    		for(int j=0; j<g[i].size(); j++) {
    			node t = g[i][j];
    			if(evis[i][t.v]==1)continue;
    			tv+=g[i][t.v].w;
    			evis[i][t.v]=evis[t.v][i]=1; //边标记为已被访问
    			if(vis[t.v]==false) {
    				q.push(t.v);
    				vis[t.v]=true; //顶点标记为已被访问
    			}
    		}
    	}
    }
    void bfs_travel() {
    	int cct=0; //统计连通分量个数
    	for(int i=1; i<=n; i++) {
    		if(vis[i]==true)continue;
    		int head=i,num=0,tv=0; //head 记录最大点权顶点;num记录顶点数;tv记录总边权
    		bfs(i,head,num,tv);
    		cct++;
    		/*
    			在此处理各个连通分量
    		*/
    	}
    }
    int main(int argc,char * argv[]) {
    	int w,a,b;
    	scanf("%d %d",&n,&k);
    	for(int i=0; i<n; i++) {
    		scanf("%d %d %d",&a,&b,&w);// 录入边描述:顶点1,顶点2,权重
    		wt[a]+=w; //将边权记录到顶点
    		wt[b]+=w;
    		g[a].push_back({b,w}); //记录边权
    		g[b].push_back({a,w});
    	}
    	bfs_travel(); //深度优先遍历
    	return 0;
    }
    

    并查集(+路径压缩)

    并查集计算连通分量属性(邻接矩阵)

    #include <iostream>
    using namespace std;
    const int maxn=2010;
    int n,k,father[maxn],wt[maxn],awt[maxn],anum[maxn],head[maxn];//wt点权,g边权
    /* 并查集 初始化 */
    void init() {
    	for(int i=1; i<=n; i++) father[i]=i;
    }
    /* 并查集 查+路径压缩 */
    int find(int x) {
    	int a = x;
    	while(x!=father[x])
    		x=father[x];
    	while(a!=father[a]) {
    		int temp=a;
    		a=father[a];
    		father[temp]=x;
    	}
    	return x;
    }
    /* 并查集 并 */
    int Union(int a, int b) {
    	int x = find(a);
    	int y = find(b);
    	if(x<=y)father[y]=x;
    	else father[x]=y;
    }
    int main(int argc,char * argv[]) {
    	init();
    		int w,a,b;
    	scanf("%d %d",&n,&k);
    	for(int i=0; i<n; i++) {
    		scanf("%d %d %d",&a,&b,&w);// 录入边描述:顶点1,顶点2,权重
    		Union(a,b);
    		wt[a]+=w; //将边权记录到顶点
    		wt[b]+=w;
    	}
    	for(int i=1; i<=n; i++) {
    		int r = find(i);
    		anum[r]++; //连通分量 总人数增加1
    		awt[r]+=wt[i]; //连通分量总边权 将边权记录到边的两个顶点中,计算总权重增加 因为有重复计算,为实际边权两倍 
    		if(wt[head[r]]<wt[i]) {
    			head[r]=i;
    		}
    	}
    	return 0;
    }
    

    并查集计算连通分量属性(邻接表)

    #include <iostream>
    #include <vector>
    using namespace std;
    const int maxn=2010;
    int n,k,father[maxn],wt[maxn];//wt点权,g边权
    struct node {	//定义每个连通块的属性
    	int anum,awt,head;	//总人数,总权重,队首整数编号
    } cc[maxn]; //连通分量数组 
    
    /* 并查集 初始化 */
    void init() {
    	for(int i=0; i<maxn; i++) father[i]=i;
    }
    /* 并查集 查 */
    int find(int x) {
    	int a = x;
    	while(x!=father[x])
    		x=father[x];
    	while(a!=father[a]) {
    		int temp=a;
    		a=father[a];
    		father[temp]=x;
    	}
    	return x;
    }
    /* 并查集 并 */
    void Union(int a, int b) {
    	int x = find(a);
    	int y = find(b);
    	if(x<=y)father[y]=x;
    	else father[x]=y;
    }
    
    int main(int argc,char * argv[]) {
    	init();
    	int w,a,b;
    	scanf("%d %d",&n,&k);
    	for(int i=0; i<n; i++) {
    		scanf("%d %d %d",&a,&b,&w);// 录入边描述:顶点1,顶点2,权重
    		Union(a,b);
    		wt[a]+=w; //将边权记录到顶点
    		wt[b]+=w;
    	}
    	for(int i=1; i<=n; i++) {
    		int r = find(i);
    		cc[r].anum++;  	//连通分量 总人数增加1
    		cc[r].awt += wt[i]; //连通分量总边权 将边权记录到边的两个顶点中,计算总权重增加 因为有重复计算,为实际边权两倍 
    		if(wt[cc[r].head]<wt[i]) {
    			cc[r].head=i;
    		}
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    通过WebRTC实现实时视频通信(三)
    通过WebRTC实现实时视频通信(二)
    通关机器学习,必须掌握的32种算法
    Github如何回退/回滚到某个版本
    Markdown语法与入门
    如何上传代码到github?
    Latex中如何设置字体颜色(3种方式)
    Package CJK Error: Invalid character code. 问题解决方法--xelatex和pdflatex编译的转换
    PyCharm快捷键
    最全PyCharm教程
  • 原文地址:https://www.cnblogs.com/houzm/p/12371427.html
Copyright © 2011-2022 走看看