PTA甲级 1013 Battle Over Cities (25分) BFS,DFS,查并集

强烈推荐,刷PTA的朋友都认识一下柳神–PTA解法大佬

本文由参考于柳神博客写成

柳神的CSDN博客,这个可以搜索文章

柳神的个人博客,这个没有广告,但是不能搜索

还有就是非常非常有用的 算法笔记 全名是

算法笔记  上级训练实战指南		//这本都是PTA的题解
算法笔记

PS 今天也要加油鸭

在这里插入图片描述

题目原文

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 cit**y1-cit**y2 and cit**y1-cit**y3. Then if cit**y1 is occupied by the enemy, we must have 1 highway repaired, that is the highway cit**y2-cit**y3.

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

生词如下:

PS:是城市之间铁路的事.但是具体的我就没有看懂.

vitally 紧急的

enemy 敌人

immediately 立刻的

the rest of 剩余的

Battle 战争

题目大意:

翻译成数据结构的话.

给定一个无向图并规定,当删除图中的某个顶点时,将会同时把与之连接的边一起删除.接下来给出K个查询,每个查询给出一个欲删除的顶点编号.求删除该顶点(包括和他相邻的边),才能使图变为连通

思路如下:

这个是算法笔记的思路,我只是实现了一下.

给一个无向图,怎么用最小的边,让整个图都联通呢.

答案就是 这样的一个图.

在这里插入图片描述

需要的条数,就是连通块减1 .

所以我们只需要求这个图的连通块就行了.

求图的连通块有两种方法。

① 是图的遍历

② 是交并集

我们这里采用图的遍历来写

有多种代码

代码如下:

① 用一个二维数组表示图.遍历方法用DFS.

代码如下:

#include<iostream>
#include<cstring>
using namespace std;
const int Max = 1024;
int N = 0, M = 0, K = 0, G[Max][Max] = { 0 }, c1 = 0, c2 = 0,currentPoint;	//current当前的节点
bool vis[Max];
void DFS(int v) {
	if (v == currentPoint)	return;						//表示删除边的情况.就直接返回
	vis[v] = true;										//经典DFS算法,的Vis符号
	for (int i = 1; i <=N; ++i) 
		if (G[v][i] == 1 && vis[i] == false)	DFS(i);	//等于1,就是有数据,这个是要会自己换的
}
int main(void) {
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 1; i <= M; ++i) {
		scanf("%d%d", &c1, &c2);
		G[c1][c2] = 1;G[c2][c1] = 1;					//因为是无向图,所以两个都需要加上.
	}
	for (int j = 1; j <= K; ++j) {
		scanf("%d", &currentPoint);					
		int block = 0;
		memset(vis, false, sizeof(vis));				//比较好用的一个填充函数
		for (int i = 1; i <= N; ++i) {
			if (i != currentPoint && !vis[i]) {
				DFS(i);
				++block;								//block就是我们算出来的连通块的个数
			}
		}
		printf("%d\n", block-1);					
	}
	return 0;
}

② 图用Vector 表示,遍历算法还是DFS

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int N, M, K,c1,c2,currentPoint;					//c1,c2是读入用的数据
												//currentPoint就是那个我们要删除的结点
const int Max = 1024;
vector<int> G[Max];								//表示图
bool vis[Max] = { false };						//判断结点是否访问
void DFS(int v) {
	if (v == currentPoint)	return;
	vis[v] = true;
	for (int i = 0; i <  G[v].size(); ++i) 
		if (!vis[G[v][i]])	DFS(G[v][i]);
}
int main(void) {
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < M; ++i) {
		scanf("%d%d", &c1, &c2);
		G[c1].push_back(c2);
		G[c2].push_back(c1);
	}
	for (int i = 0; i < K; ++i) {
		int block = 0;
		scanf("%d", &currentPoint);
		memset(vis, false, sizeof(vis));
		for (int j = 1; j <= N; ++j) {
			if (currentPoint != j && !vis[j]) {
				DFS(j);
				block++;
			}
		}
		printf("%d\n", block - 1);
	}
	return 0;
}

③ 图还是用二维数组,但是用BFS

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int Max = 1024;
int G[Max][Max] = {0}, N, M, K, currentPoint, c1, c2;		//G表示图
					//currentPoint就是我们要删除的结点,c1,c2就是替换数
bool vis[Max];		//vis就是判断有没有访问过了
void BFS(int v) {
	if (currentPoint == v)	return;
	queue<int> q;		//定义一个队列
	q.push(v);			
	vis[v] = true;
	while (q.size() != 0) {			//如果q的队列不为空的话
		v=q.front();				//每次访问的都是v啊		
		q.pop();
		for (int i = 1; i <= N; ++i) {
			if (G[v][i] == 1 && vis[i] == false&&i!=currentPoint) {
				vis[i] = true;
				q.push(i);
			}
		}
	}
}
int main(void) {
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < M; ++i) {
		scanf("%d%d", &c1, &c2);
		G[c1][c2] = 1; G[c2][c1] = 1;
	}
	for (int i = 0; i <K; ++i) {
		scanf("%d", &currentPoint);
		int block = 0;
		memset(vis, false, sizeof(vis));
		for (int j = 1; j <= N; ++j) {
			if (vis[j] == false&&j!=currentPoint) {
				BFS(j);
				++block;
			}
		}
		printf("%d\n", block - 1);
	}
	return 0;
}

④图用vector表示,遍历算法是BFS

#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
const int Max = 1024;
vector<int> G[Max];
bool vis[Max];
int N, M, K, c1, c2, currentPoint;
void BFS(int v) {
	if (v == currentPoint)	return;
	queue<int> q;
	q.push(v);
	vis[v] == true;
	while (q.size() != 0) {
		v = q.front();
		q.pop();
		for (int i = 0; i <G[v].size(); ++i) {
			if (vis[G[v][i]] == false && G[v][i] != currentPoint) {
				q.push(G[v][i]);
				vis[G[v][i]] = true;
			}
		}
	}
}
int main(void) {
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < M; ++i) {
		scanf("%d%d", &c1, &c2);
		G[c1].push_back(c2);
		G[c2].push_back(c1);
	}
	for (int i = 0; i < K; ++i) {
		scanf("%d", &currentPoint);
		int block = 0;
		memset(vis, false, sizeof(vis));
		for (int j = 1; j <= N; ++j) {
			if (vis[j] == false && j != currentPoint) {
				BFS(j);
				++block;
			}
		}
		printf("%d\n", block - 1);
	}
	return 0;
}

⑤ 下面写下并查集的方法

#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int N = 1111;
vector<int> G[N];				//图
int father[N];					//存放父亲结点
bool vis[N];					//记录结点是否被访问
int findFather(int x) {			//查找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) {		//合并a和b所在的集合
	int faA = findFather(a);
	int faB = findFather(b);
	if (faA != faB) {
		father[faA] = faB;
	}
}
//初始化father数组与hashTable数组
void init() {
	for (int i = 1; i < N; ++i) {
		father[i] = i;
		vis[i] = false;
	}
}
int n, m, k;
int main(void) {
	scanf("%d%d%d", &n, &m, &k);		//输入顶点数,边数,和查询数
	for (int i = 0; i < m; ++i) {
		int a, b;
		scanf("%d%d", &a, &b);			//输入边的两个顶点
		G[a].push_back(b);				//边 a->b
		G[b].push_back(a);				//边 b->a
	}
	int currentPoint;					//当前要删除的顶点编号
	for (int query = 0; query < k; ++query) {		//k次查询
		scanf("%d", &currentPoint);
		init();		//初始化father数组与hashTable数组
		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;								//连通块个数加1
				vis[fa_i] = true;						//当前连通块的根结点设为已访问
			}
		}	
		printf("%d\n", block - 1);						//输出连通块的个数减一,即需要增加的边
	}
	return 0;
}

如果这篇文章对你有张帮助的话,可以用你高贵的小手给我点一个免费的赞吗

相信我,你也能变成光.

在这里插入图片描述

如果你有任何建议,或者是发现了我的错误,欢迎评论留言指出.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值