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