codeup 习题--图的遍历-- 凌宸1642
codeup 习题--图的遍历-- 凌宸1642
问题A : 第一题
题目描述 :
该题的目的是要你统计图的连通分支数。
输入描述:
每个输入文件包含若干行,每行两个整数 i , j ,表示节点 i 和 j 之间存在一条边。
输出描述
输出每个图的联通分支数。
样例输入:
1 4
4 3
5 5
样例输出:
2
DFS 解法
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1000010 ;
vector<int> Adj[maxn] ;
bool vis[maxn] = { false } ; // 是否被访问过
bool in[maxn] = { false } ; // 是否是题目中出现过的结点编号
int ans = 0 , n = 0 ; // ans记录答案,n记录最大结点编号
void DFS(int u){
vis[u] = true ;
for(int i = 0 ; i < Adj[u].size() ; i ++){
int v = Adj[u][i] ;
if(vis[v] == false)
DFS(v) ;
}
}
void DFSTrave(){
for(int i = 0 ; i <= n ; i ++){
if(in[i] == true && vis[i] == false){
DFS(i) ;
ans ++ ;
}
}
}
int main(){
int i , j ;
while(scanf("%d%d" , &i , &j) != EOF){
if(i > n) n = i ;
if(j > n) n = j ;
Adj[i].push_back(j) ; // 添加边 i -> j
Adj[j].push_back(i) ; // 添加边 j -> i
in[i] = in[j] = true ; // 表明 i j 顶点均是本题输入中的顶点
}
DFSTrave() ;
printf("%d\n" , ans) ;
return 0;
}
/**************************************************************
Problem: 1798
User: 2965666021
Language: C++
Result: 正确
Time:196 ms
Memory:31532 kb
****************************************************************/
BFS 解法
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1000010 ;
vector<int> Adj[maxn] ;
bool inq[maxn] = { false } ; // 是否已入过队问过
bool in[maxn] = { false } ; // 是否是题目中出现过的结点编号
int ans = 0 , n = 0 ; // ans记录答案,n记录最大结点编号
void BFS(int u){
queue<int> q ; // 定义队列
q.push(u) ; // 元素入队
inq[u] = true ; // 设置 u 已入队
while(!q.empty()){
int v = q.front() ; // 获取队首元素
q.pop() ; // 队首元素出队
for(int i = 0 ; i < Adj[v].size() ; i ++){ // 对 队首元素 u 的所有能到达的顶点 s
int s = Adj[v][i] ;
if(inq[s] == false){ // 如果 s 还未入队
q.push(s) ; // 将 s 入队
inq[s] = true ; // 设置 s 已入队
}
}
}
}
void BFSTrave(){
for(int i = 0 ; i <= n ; i ++){ // 遍历所有顶点
if(in[i] == true && inq[i] == false){
BFS(i) ; // 没调用一次 BFS 访问完产生一个连通块
ans ++ ; // 连通块的数目加 1
}
}
}
int main(){
int i , j ;
while(scanf("%d%d" , &i , &j) != EOF){
if(i > n) n = i ;
if(j > n) n = j ;
Adj[i].push_back(j) ; // 添加边 i -> j
Adj[j].push_back(i) ; // 添加边 j -> i
in[i] = in[j] = true ; // 表明 i j 顶点均是本题输入中的顶点
}
BFSTrave() ;
printf("%d\n" , ans) ;
return 0;
}
/**************************************************************
Problem: 1798
User: 2965666021
Language: C++
Result: 正确
Time:218 ms
Memory:31544 kb
****************************************************************/
并查集解法
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1000010 ;
int father[maxn] = {0} ;
bool in[maxn] = { false } ; // 是否是题目中出现过的结点编号
int n , m ;
// 查找根结点
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 <= maxn ; i ++){
father[i] = i ;
}
}
int main(){
int a , b , n = 0;
init() ;
while(scanf("%d%d" , &a , &b)!=EOF){
Union(a , b) ;
n = max(n , max(a, b)) ;
in[a] = in[b] = true ;
}
int ans = 0 ;
for(int i = 1 ; i <= n ; i ++){
if(in[i] == true &&father[i] == i)
ans ++ ;
}
printf("%d\n" , ans) ;
return 0;
}
/**************************************************************
Problem: 1798
User: 2965666021
Language: C++
Result: 正确
Time:36 ms
Memory:7060 kb
****************************************************************/
PS: 并查集解法明显消耗的内存和运行时间更优。
问题 B: 连通图
题目描述:
给定一个无向图和其中的所有边,判断这个图是否所有顶点都是连通的。
输入描述:
每组数据的第一行是两个整数 n 和 m(0<=n<=1000)。n 表示图的顶点数目,m 表示图中边的数目。如果 n 为 0 表示输入结束。随后有 m 行数据,每行有两个值 x 和 y(0<x, y <=n),表示顶点 x 和 y 相连,顶点的编号从 1 开始计算。输入不保证这些边是否重复。
输出描述:
对于每组输入数据,如果所有顶点都是连通的,输出"YES",否则输出"NO"。
样例输出:
4 3
4 3
1 2
1 3
5 7
3 5
2 3
1 3
3 2
2 5
3 4
4 1
7 3
6 2
3 1
5 6
0 0
样例输出:
YES
YES
NO
DFS 解法
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1010 ;
vector<int> Adj[maxn] ; // 邻接表
bool vis[maxn] ; // 是否已访问数组
int n , m ;
void DFS(int u){
vis[u] = true ; // 设置 u 已访问
for(int i = 0 ; i < Adj[u].size() ; i ++){
int v = Adj[u][i] ;
if(vis[v] == false)
DFS(v) ;
}
}
void DFSTrave(){
DFS(1) ; // 只需从任意一个起点出发,如果能一次遍历之后,整个图中的所有顶点都已被访问,则是连通图
bool tag = true ;
for(int i = 1 ; i <= n ; i ++){
if(vis[i] == false){
tag = false ;
break ;
}
}
printf("%s\n" , (tag)?"YES":"NO");
}
int main(){
while(scanf("%d%d" , &n , &m) , n){
memset(vis , false , sizeof(vis)) ; // 对访问数组初始化
int a , b ;
for(int i = 0 ; i < m ; i ++){
scanf("%d%d" , &a , &b) ;
Adj[a].push_back(b) ;
Adj[b].push_back(a) ;
}
if( m < n - 1) printf("NO\n"); // 连通图的性质 边数 E >= (顶点数 - 1)
else DFSTrave() ;
for(int i = 1; i <= n; i++) Adj[i].clear(); //记得要初始化
}
return 0;
}
/**************************************************************
Problem: 1908
User: 2965666021
Language: C++
Result: 正确
Time:4 ms
Memory:2204 kb
****************************************************************/
BFS 解法
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1010 ;
vector<int> Adj[maxn] ; // 邻接表
bool inq[maxn] ; // 是否已入队数组
int n , m ;
void BFS(int u){
queue<int> q ;
q.push(u) ; // 将 u 入队
inq[u] = true ; // 设置 u 已入队
while(!q.empty()){
int u = q.front() ; // 获得队首元素
q.pop() ; // 队首元素出队
for(int i = 0 ; i < Adj[u].size() ; i ++){
if(inq[Adj[u][i]] == false){
q.push(Adj[u][i]) ;
inq[Adj[u][i]] = true ;
}
}
}
}
void BFSTrave(){
BFS(1) ;
bool tag = true ;
for(int i = 1 ; i <= n ; i ++){
if(inq[i] == false){
tag = false ;
break ;
}
}
printf("%s\n" , (tag)?"YES":"NO");
}
int main(){
while(scanf("%d%d" , &n , &m) , n){
memset(inq , false , sizeof(inq)) ; // 对访问数组初始化
int a , b ;
for(int i = 0 ; i < m ; i ++){
scanf("%d%d" , &a , &b) ;
Adj[a].push_back(b) ;
Adj[b].push_back(a) ;
}
if( m < n - 1) printf("NO\n");
else BFSTrave() ;
for(int i = 1; i <= n; i++) Adj[i].clear(); //记得要初始化
}
return 0;
}
/**************************************************************
Problem: 1908
User: 2965666021
Language: C++
Result: 正确
Time:4 ms
Memory:2216 kb
****************************************************************/
并查集解法
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1010 ;
int father[maxn] ;
int n , m ;
// 查找根结点
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(int n){
for(int i = 1 ; i <= n ; i ++){
father[i] = i ;
}
}
int main(){
while(scanf("%d%d" , &n , &m) , n){
init(n) ; // 初始化
int a , b ;
for(int i = 0 ; i < m ; i ++){
scanf("%d%d" , &a , &b) ;
Union(a , b) ;
}
if(m < n - 1) printf("NO\n"); // 连通图的性质 边数 E >= (顶点数 - 1)
else{
int ans = 0 ;
for(int i = 1 ; i <= n ; i ++){
if(i == father[i])
ans ++ ;
printf("%s\n" , (ans == 1)?"YES":"NO");
}
}
return 0;
}