vector<int>G[1010];
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;//--------写到这,手又忍不住要写Union(a,b)了- -,无语
G[a].push_back(b);//建立关系
G[b].push_back(a);
}
搜联通块搜到当前访问的顶点编号是删除的城市编号,就退出,此连通块遍历结束
继续下一个连通块的遍历
for(int j=1;j<=n;j++){//The cities are numbered from 1 to N.
if(j!=city&&vis[j]==false){//不是被删除并且没被访问过的城市//遍历不是删除城市的顶点
dfs(j);//遍历顶点i所在的连通块,depth没用,就没写//写到这,又想写if条件,重新init(),Union(),计算联通块数了
cnt++;
}
}
for(int i=0;i<q;i++){
cin>>city;
memset(vis,false,sizeof(vis));//恢复原状
int cnt=0;
//1 2 3//如何求1.多个2.没有某个顶点的图的联通块数?//
for(int j=1;j<=n;j++){//The cities are numbered from 1 to N.
if(j!=city&&vis[j]==false){//不是被删除并且没被访问过的城市//遍历不是删除城市的顶点
dfs(j);//遍历顶点i所在的连通块,depth没用,就没写//写到这,又想写if条件,重新init(),Union(),计算联通块数了
cnt++;
}
}
cout<<cnt-1<<endl;
}
void dfs(int index){
if(index==city)return;//当前遍历到的城市编号等于需要删除的城市的编号
vis[index]=true;
for(int j=0;j<G[index].size();j++){
if(vis[G[index][j]]==false){
dfs(G[index][j]);
}
}
}
//图的遍历
//给定1个无向图并规定:
//当删除图中某个顶点的时候,与之链接的边一起删除
//接下来给出k个查询
//每个查询给出1个欲删除的顶点编号,求删除该顶点(和与其链接的边)后需要增加多少条便,才能让图联通
```cpp
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
int n,m,q;
vector<int>G[1010];
bool vis[1010];
int city;
void dfs(int index){
if(index==city)return;//当前遍历到的城市编号等于需要删除的城市的编号
vis[index]=true;
for(int j=0;j<G[index].size();j++){
if(vis[G[index][j]]==false){
dfs(G[index][j]);
}
}
}
int main()
{
cin>>n>>m>>q;
for(int i=0;i<m;i++){
int a,b;
cin>>a>>b;//--------写到这,手又忍不住要写Union(a,b)了- -,无语
G[a].push_back(b);//建立关系
G[b].push_back(a);
}
for(int i=0;i<q;i++){
cin>>city;
memset(vis,false,sizeof(vis));//恢复原状
int cnt=0;
//1 2 3//如何求1.多个2.没有某个顶点的图的联通块数?//
for(int j=1;j<=n;j++){//The cities are numbered from 1 to N.
if(j!=city&&vis[j]==false){//不是被删除并且没被访问过的城市//遍历不是删除城市的顶点
dfs(j);//遍历顶点i所在的连通块,depth没用,就没写//写到这,又想写if条件,重新init(),Union(),计算联通块数了
cnt++;
}
}
cout<<cnt-1<<endl;
}
return 0;
}
//我们遍历整个图,就需要对所有的连通块分别进行遍历
//如果给定的图是一个连通图,则只需要一次dfs就能遍历完图---注意啊,是说在main()调用一次dfs,不用for所有顶点来试了
把每个顶点当作根节点来遍历,来寻找连通块(标记访问的节点,下次不访问)
// //沿着一条路径直到无法继续前进
// //才退回到路径上里当前顶点最近的&还存在未访问的岔道口
// //并前往访问那些未访问的分支顶点
// //直到遍历完这个图
//3 2 3–n,m,k------城市数,道路数,需要检查(删除)的城市数---------------这咋又让我想到了红色警报
//随后m行
//1 2-----某条道路2端的2个城市编号
//1 3-----某条道路2端的2个城市编号
//1 2 3--------------k个(3个)需要删除的城市编号–检查
//----------------
//1
//0
//0
//A.需要加几条边,使得整个图联通
//添加的边数=联通块数-1-----------------------------》让我想到了father[faA]=faB,并查集是合并2棵没有交集的树,有交集不行
//B.求无向图G的连通块数---------------》无向图----》我是你的好朋友,那你也是我的好朋友的感jio
//a.图的遍历
//b.并查集
//1.图的遍历dfs
//由于是无向图,因此在读入数据时,要把2个方向的边都进行存储---------------让我想到了 排座位 的二维数组 记录法
#include <cstdio>
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int n, m, q,city;
vector<int> G[1010];//邻接表
bool vis[10010];
void dfs(int v){
if (v == city)return;
vis[v] = true;
for (int i = 0; i < G[v].size(); i++){//--------和静态树的遍历意昂
if (vis[G[v][i] == false])
dfs(G[v][i]);
}
}
int main() {
cin >> n >> m >> q;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
G[a].push_back(b);
//让我想到了树的 静态写法,只不过那是node[父节点编号].push_back(子节点编号)
//而这个双向的,树的话只能从上到下遍历---dfs
G[b].push_back(a);
}
for (int i = 0; i < q; i++) {
//int city;
cin >> city;
memset(vis, false, sizeof(vis));//初始化vis数组为false
int block = 0;//连通块个数,初始化为0
for (int i = 1; i <= n; i++) {//枚举每个顶点
if (i != city && vis[i] == false) {
dfs(i);//遍历顶点i所在的连通块
block++;//联通块数+1----------
//他前面遍历一定做了标记-哪些访问过了,后来筛选进入遍历的城市,有条件控制block++
}
}
cout << block - 1<<endl;
}
return 0;
}
//2.并查集
//问题:给一个图,图中有n个顶点,m条边,求图中连通块的个数。
//以下有两种方法可用:1.DFS 2.并查集
//1.用邻接矩阵存图,dfs找连通块数
.#include<bits/stdc++.h>
const int N=1005;
using namespace std;
vector<int> t[N];
int vis[N]={0};
void dfs(int k)
{
for(int i=0;i<t[k].size();i++)
{
if(vis[t[k][i]]==0)
{ vis[t[k][i]]=1;
dfs(t[k][i]);
}
}
}
int main()
{
int n,m;//n个顶点,m条边
cin>>n>>m;
int a,b;//边所连接的两个顶点
for(int i=1;i<=m;i++)
{
cin>>a>>b;
t[a].push_back(b);
t[b].push_back(a);
}
int ans=0;//记录连通块数量
for(int i=1;i<=n;i++)
{
if(vis[i]==0)
{
vis[i]=1;
dfs(i);
ans++;
}
}
cout<<ans<<endl;
return 0;
}
2.并查集思想,每个顶点找其父节点(find函数),若父节点相同,则属于同一个连通块。
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m;
vector<int> t[N];
int pre[N],root[N];
void init(int nn)
{
for(int i=1;i<=nn;i++)
pre[i]=i;
}
int find(int x)
{
int f=x;
while(x!=pre[x])//找到x的父节点
x=pre[x];
int j;
while(f!=pre[f])//依次将x上面的节点的父节点赋为x;
{
j=f;
f= pre[f];
pre[j]=x;
}
return x;
}
void merge(int a,int b)
{
int t1,t2;
t1=find(a);
t2=find(b);
if(t1!=t2)
{
pre[t2]=t1;
}
}
int block(int nn)
{
int ans=0;
for(int i=1;i<=nn;i++)
root[pre[i]]=1;
for(int i=1;i<=nn;i++)
ans+=root[i];
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
int a,b;//边所连接的两个顶点
init(n);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
t[a].push_back(b);
t[b].push_back(a);
merge(a,b);
}
printf("%d",block(n));
return 0;
}