刷題筆記-圖-統計連通分量


統計連通分量思路

統計連通分量的個數及其他屬性(如:總點權,總邊權等),思路有三種: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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM