题目
题目大意
给定一个城市图,如果攻陷一个城市,该城市连接的所有路都要被销毁。要求计算出连通剩余的城市最少需要修建几条路。该图有n个顶点,m条边,k个重点城市。分别求出每个重点城市被攻陷,连通剩余城市需要修建的路。
思路
求连通剩余城市需要修建的路,需要知道剩余城市的连通分量。连通分量可以看作一个连通的子图,单个顶点(没有任何边相连)也是一个连通分量。连通分量数 - 1 = 连通剩余图需要的最少边数。
所以关键就是求连通分量数,可以用dfs深度递归遍历来解决。从某一个顶点开始深度递归,直到递归不动,说明此时到达了该连通分量的终点。即每一次dfs都可以找到一个连通分量。为了避免重复递归,可以加一个bool数组,如果被递归过了就设为true,每次递归只遍历值为false的。而被攻陷的城市就可以看作已经被遍历过了,标记为true。
知识点
可变二维数组:vector<int> v[n]。
在C++中,数组作为函数参数传递时,默认传递的是数组的地址,因此不需要显式使用引用符号(&)。这不同于传递一个 vector 或其他类对象,这些通常按值传递,必须用引用符号 & 来避免复制。
代码
#include <iostream>
#include <vector>
using namespace std;
int n, m, k;
int g[1001][1001] = {0};
void dfs(int vi, bool b[]){ // 数组传入不用加引用,因为直接传入了地址
b[vi] = true;
for (int i = 1; i <= n; i++){
if (g[vi][i] && !b[i]){
dfs(i, b);
}
}
} // 深度递归遍历,将遍历到的顶点设为访问过
int main(){
cin >> n >> m >> k;
for (int i = 0; i < m; i++){
int v1, v2;
cin >> v1 >> v2;
g[v1][v2] = g[v2][v1] = 1;
} // 构建图
for (int i = 0; i < k; i++){
bool b[1001] = {false}; // 每个顶点是否被访问过;不要设为全局变量,否则每次循环还要手动进行初始化
int vi;
cin >> vi;
b[vi] = true; // 标记该顶点被访问过
int cnt = 0; // 剩余图的连通分量的个数
for (int j = 1; j <= n; j++){
if (!b[j]){
dfs(j, b); // 每次调用完相当于遍历完一个连通分量
cnt++;
}
}
cout << cnt - 1 << endl; // 需要添加的边数 = 连通顶点数 - 1
}
return 0;
}