刷题笔记-图-统计连通分量
目录
统计连通分量思路
统计连通分量的个数及其他属性(如:总点权,总边权等),思路有三种: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;
}
标签:
刷题笔记-图
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!