# 1 题目

1013 Battle Over Cities (25分)
It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.

For example, if we have 3 cities and 2 highways connecting city1-city2​ and city​1-city3. Then if city1​ is occupied by the enemy, we must have 1 highway repaired, that is the highway city2​ -city3.

Input Specification:
Each input file contains one test case. Each case starts with a line containing 3 numbers N (<1000), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.

Output Specification:
For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.

Sample Input:
3 2 3
1 2
1 3
1 2 3

Sample Output:
1
0
0


# 2 解析

## 2.2 思路

• 无向图中添加多少条边，才能使得图变得连通？答案：图中连通块个数 - 1

### 2.2.1思路一

• 用DFS或BFS遍历图得到图中的连通块个数；
• 由于是无向图，读入边时，需要对两个方向的边都进行存储

### 2.2.2 思路二

• 用并查集求连通块个数；
• 判断无向图中两个顶点是否在同一个集合中，如果在同一个集合内，则不做处理，否则，将将两个顶点加入同一个集合中，最后统计有集合的个数

# 3 参考代码

## 3.1 图遍历

• 邻接矩阵实现
#include <cstdio>
#include <queue>
#include <cstring>

using std::queue;

const int MAXV = 1010;
int G[MAXV][MAXV] = {0};
bool inq[MAXV] = {false};

int N;
int concernCity;

void BFS(int u){
inq[concernCity] = true;//标记结点concernCity为不可达

queue<int> q;
q.push(u);
inq[u] = true;
while(!q.empty()){
int now = q.front();
q.pop();

for (int v = 1; v <= N; ++v)
{
if(G[now][v] == 1 && inq[v] == false){
q.push(v);
inq[v] = true;
}
}
}
}

void DFS(int u){
if(u == concernCity)//当遍历到已删除结点时，返回
return;

inq[u] = true;
for (int i = 1; i <= N; ++i) {//遍历u的所有邻接结点
if(inq[i] == false && G[u][i] == 1){
DFS(i);
}
}
}

int main(int argc, char const *argv[])
{
int m, k;
scanf("%d%d%d", &N, &m, &k);//顶点个数、边数、查询数

int e1, e2;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &e1, &e2);
G[e1][e2] = 1;//创建无向图
G[e2][e1] = 1;
}

int block;//连通块个数
for (int i = 0; i < k; ++i)
{
block = 0;
memset(inq, false, sizeof(inq));//初始化inq数组

scanf("%d", &concernCity);//欲删除顶点编号

for (int j = 1; j <= N; ++j) {
if(j != concernCity && inq[j] == false){
//BFS(j);
DFS(j);//遍历j所在的连通块
block++;//连通块个数+1
}
}

printf("%d\n", block - 1);
}

return 0;
}

• 邻接表
#include <cstdio>
#include <queue>
#include <cstring>
#include <vector>

using std::queue;
using std::vector;

const int MAXV = 1010;
vector<int> G[MAXV];
bool inq[MAXV] = {false};

int N;
int concernCity;

void BFS(int u){
inq[concernCity] = true;//标记结点concernCity为不可达

queue<int> q;
q.push(u);
inq[u] = true;
while(!q.empty()){
int now = q.front();
q.pop();

for (int v = 0; v < G[now].size(); ++v)
{
if(inq[G[now][v]] == false){
q.push(G[now][v]);
inq[G[now][v]] = true;
}
}
}
}

void DFS(int u){
if(u == concernCity)//当遍历到已删除结点时，返回
return;

inq[u] = true;
for (int i = 0; i < G[u].size(); ++i) {//遍历u的所有邻接结点
if(inq[G[u][i]] == false){
DFS(G[u][i]);
}
}
}

int main(int argc, char const *argv[])
{
int m, k;
scanf("%d%d%d", &N, &m, &k);//顶点个数、边数、查询数

int e1, e2;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &e1, &e2);
G[e1].push_back(e2);//创建无向图
G[e2].push_back(e1);
}

int block;//连通块个数
for (int i = 0; i < k; ++i)
{
block = 0;
memset(inq, false, sizeof(inq));//初始化inq数组

scanf("%d", &concernCity);//欲删除顶点编号

for (int j = 1; j <= N; ++j) {
if(j != concernCity && inq[j] == false){
BFS(j);
//DFS(j);//遍历j所在的连通块
block++;//连通块个数+1
}
}

printf("%d\n", block - 1);
}

return 0;
}


## 3.2 并查集

#include <cstdio>
#include <vector>

using std::vector;

const int MAXV = 1010;
vector<int> G[MAXV];

int father[MAXV];
bool vis[MAXV];

int findFather(int x){
int a = x;
while(x != father[x]){
x = father[x];
}

while(a != father[a]){
int z = a;
a = father[a];
father[z] = x;
}

return x;
}

void Union(int a, int b){
int faA = findFather(a);
int faB = findFather(b);
if(faA != faB){
father[faA] = faB;
}
}

void init(){
for (int i = 1; i < MAXV; ++i)
{
father[i] = i;
vis[i] = false;
}
}

int main(int argc, char const *argv[])
{
int n, m,k;
scanf("%d%d%d", &n, &m, &k);

int a, b;
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
G[a].push_back(b);//构建无向图
G[b].push_back(a);
}

int currentPoint;
for (int query = 0; query < k; ++query)
{
scanf("%d", &currentPoint);//当前需要删除的结点编号
init();//初始化father数组和vis数组

for (int i = 1; i <= n; ++i)
{
for (int j = 0; j < G[i].size(); ++j)//枚举每条边
{
int u = i, v = G[i][j];//边的两个端点u，v
if(u == currentPoint || v == currentPoint){
continue;
}

Union(u, v);//合并u和v所在集合
}
}
int block = 0;//连通块个数
for (int i = 1; i <= n; ++i)//遍历所有结点
{
if(i == currentPoint){
continue;
}

int fa_i = findFather(i);//顶点i所在的根结点为fa_i

if(vis[fa_i] == false){//如果当前连通块的根结点未被访问
block++;
vis[fa_i] = true;
}
}
printf("%d\n", block - 1);//连通块个数-1为所增加的边数
}

return 0;
}


06-12 146

07-12 1812
09-19 82
05-06 1231
08-23 126
01-24 62