[문제]

방향 없는 그래프가 주어졌을 때, 연결 요소 (Connected Component)의 개수를 구하는 프로그램을 작성하시오.

 

[입력]

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주어진다.

 

[출력]

첫째 줄에 연결 요소의 개수를 출력한다.

 

[예제]


>문제 풀이

 dfs로 깊이 탐색을 해주면서 한 그룹별로 cnt++를 해주면 되는 문제.

주의할 점은 간선의 연결 없이 혼자 있는 노드도 cnt++를 해줘야 한다는 점!!

(처음에 이거 처리 안해서 틀렸었다...)

 

bfs로 풀어도 상관 없으나, 나는 dfs로 풀었다.

 

>전체 코드

 

import java.util.*;

public class Main {
	static int N, M, cnt=0;
	static int mat[][], visited[];
	
	public static void main(String [] args) {
		int p1, p2;
		
		Scanner scan= new Scanner(System.in);
		
		N= scan.nextInt();
		M= scan.nextInt();
		mat= new int[N][N];
		visited= new int[N];
		
		for(int i=0; i<M; i++) {
			p1= scan.nextInt()-1;
			p2= scan.nextInt()-1;
			mat[p1][p2]=1;
			mat[p2][p1]=1;
		}
		
		for(int i=0; i<N; i++) {
			if(visited[i]!=1) {
				dfs(i);
				cnt++;
			}
		}
		System.out.println(cnt);
		
	}//main
	
	public static void dfs(int x) {
		visited[x]=1;
		for(int i=0; i<N; i++) {
			if(mat[x][i]==1&&visited[i]!=1) {
				dfs(i);
			}
		}
	}//end dfs
	
}

www.acmicpc.net/problem/11724

 

11724번: 연결 요소의 개수

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주

www.acmicpc.net

 

 

+) 단계별 풀어보기에서 어떤 알고리즘인지는 파악했으나, 구현, 응용 및 해결 능력이 부족한 것 같아서 DFS, BFS 문제를 추가로 더 풀어보려고 한다.. 그 후에는 프로그래머스 문제를 풀어볼 것이다.

[문제]

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추근처에 서식하며 해충을 잡아 먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다.

(한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있다고 간주한다)

한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다.

예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 5마리의 배추흰지렁이가 필요하다.

(0은 배추가 심어져 있지 않은 땅이고, 1은 배추가 심어져 있는 땅을 나타낸다.)

1 1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
0 0 1 1 0 0 0 1 1 1
0 0 0 0 1 0 0 1 1 1

 

[입력]

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그 다음 K줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다.

 

[출력]

각 테스트 케이스에 대해 필요한 최소의 배추흰지렁이 마리 수를 출력한다.

 

[예제]


>문제 풀이

dfs로 풀었습니다.

테스트 케이스 T번 반복하면서 주어진 배추 배열에 따라 카운팅 및 visited를 체크해주면 되는 문제였습니다.

 

 

>전체 코드

 

import java.util.Scanner;

public class Main {
	static int N, M, cnt=0;
	static int visited[][], mat[][];
	
	public static void main(String [] args) {
		int T, K, a, b;
		
		Scanner scan= new Scanner(System.in);
		
		T=scan.nextInt();
		
		while(T-->0) {
			N= scan.nextInt();   //입력받기
			M= scan.nextInt();
			K= scan.nextInt();
			visited= new int[N][M];
			mat= new int[N][M];
			
			cnt=0;   //cnt 초기화
			
			for(int i=0; i<K; i++) {  //배추 배열 받기
				a= scan.nextInt();
				b= scan.nextInt();
				mat[a][b]= 1;
			}
			
			for(int i=0; i<N; i++) {
				for(int j=0; j<M; j++) {
					if(mat[i][j]==1&&visited[i][j]!=1) {  //1이고 방문한적없으면
						cnt++;      //카운팅하고
						dfs(i, j);  //인접하는 배추들을 visited에 넣기 위해 dfs
					}
				}
			}//end
			System.out.println(cnt);
		}//while
		
	}
	
	static int[] dx= {-1, 0, 0, 1};
	static int[] dy= {0, -1, 1, 0};
	public static void dfs(int x, int y) {
		int nowx, nowy;
		visited[x][y]=1;
		
		for(int i=0; i<4; i++) {
			nowx= x+dx[i];
			nowy= y+dy[i];
			if(nowx>=0&&nowx<N&&nowy>=0&&nowy<M) {
				if(mat[nowx][nowy]==1&&visited[nowx][nowy]!=1) {
					dfs(nowx, nowy);
				}
			}
		}
		
	}//dfs
	
}

www.acmicpc.net/problem/1012

 

1012번: 유기농 배추

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 

www.acmicpc.net

 

[문제]

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여기서 연결되었다는 것은 어떤 집이 좌우, 혹은 아래위로 다른 집이 있는 경우를 말한다. 대각선상에 집이 있는 경우는 연결된 것이 아니다. <그림 2>는 <그림 1>을 단지별로 번호를 붙인 것이다. 지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램을 작성하시오.

 

[입력]

첫 번째 줄에는 지도의 크기 N(정사각형이므로 가로와 세로의 크기는 같으며 5≤N≤25)이 입력되고, 그 다음 N줄에는 각각 N개의 자료(0혹은 1)가 입력된다.

 

[출력]

첫 번째 줄에는 총 단지수를 출력하시오.

그리고 각 단지내 집의 수를 오름차순으로 정렬하여 한 줄에 하나씩 출력하시오.

 

[예제]


>문제 풀이

 dfs 코드가 더 간단한 것 같아서 dfs로 풀었습니다. 아직 bfs는 로직이 눈에 잘 안익네요..ㅎㅎ

 

int N     //지도의 크기

int cnt   //연결된 집의 개수를 체크하기 위한 변수

int mat[][]      //입력 배열

int visited[][]   //방문 여부 체크

 

입력값은 띄어쓰기가 안된채로 들어오기 때문에 String으로 받은 후에 Integer.parseInt(charAt()) 으로 캐스팅하여 mat[][]배열에 넣어주었습니다.

그리고 mat[][]==1이고 visited!=1인 i와 j를 dfs로 넘겨서 상하좌우에 연결된 집들을 체크했습니다.

 

dfs(x, y)

들어온 x와 y에 대하여 visited[x][y]=1로 바꿔주고(1이면 방문했다는 뜻), cnt++; 카운팅을 해줍니다.

그리고 상하 좌우 값들인 0≤(x-1, y), (x, y-1), (x+1, y), (x, y+1)<N 을 체크하여 dfs로 recursion 해줍니다.

 

 

>전체 코드

 

import java.util.ArrayList;
import java.util.Scanner;

public class Main {
	static int N, cnt=0;
	static int visited[][], mat[][];
	
	public static void main(String [] args) {
		String str;
		ArrayList<Integer> answer= new ArrayList<>(); //정답을 담을 리스트
		
		Scanner scan= new Scanner(System.in);
		
		N= scan.nextInt();
		visited= new int[N][N];
		mat= new int[N][N];
		
		for(int i=0; i<N; i++) {  //문자열로 입력받고 char 하나씩 배열로
			str= scan.next();
			for(int j=0; j<N; j++) {
				mat[i][j]= Integer.parseInt(str.charAt(j)+"");
			}
		}
		
		for(int i=0; i<N; i++) {
			for(int j=0; j<N; j++) {
				if(mat[i][j]==1&&visited[i][j]!=1) { //matrix 값이 1인데, 방문한적 없으면
					cnt=0;
					dfs(i, j);
					answer.add(cnt);
				}
			}
		}//end
		
		System.out.println(answer.size());
		answer.sort(null);
		for(int i=0; i<answer.size(); i++) {
			System.out.println(answer.get(i));
		}
	}
	
	static int[] dx= {-1, 0, 0, 1}; //x값에 대한 증감
	static int[] dy= {0, -1, 1, 0}; //y값에 대한 증감
	public static void dfs(int x, int y) {
		int nowx, nowy;
		visited[x][y]=1;
		cnt++;
		
		for(int i=0; i<4; i++) { //상하좌우의 값들을 확인하기 위해
			nowx= x+dx[i];
			nowy= y+dy[i];
			if(nowx>=0&&nowx<N&&nowy>=0&&nowy<N) { //0<=nowx<N && 0<=nowy<N
				if(mat[nowx][nowy]==1&&visited[nowx][nowy]!=1) {
					dfs(nowx, nowy);
				}
			}
		}
	}//dfs
	
}

www.acmicpc.net/problem/2667

 

2667번: 단지번호붙이기

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여

www.acmicpc.net

 

[문제]

신종 바이러스인 웜 바이러스는 네트워크를 통해 전파된다. 한 컴퓨터가 웜 바이러스에 걸리면 그 컴퓨터와 네트워크 상에서 연결되어 있는 모든 컴퓨터는 웜 바이러스에 걸리게 된다.

예를 들어 7대의 컴퓨터가 <그림 1>과 같이 네트워크 상에서 연결되어 있다고 하자. 1번 컴퓨터가 웜 바이러스에 걸리면 웜 바이러스는 2번과 5번 컴퓨터를 거쳐 3번과 6번 컴퓨터까지 전파되어 2, 3, 5, 6 네 대의 컴퓨터는 웜 바이러스에 걸리게 된다. 하지만 4번과 7번 컴퓨터는 1번 컴퓨터와 네트워크상에서 연결되어 있지 않기 때문에 영향을 받지 않는다.

어느 날 1번 컴퓨터가 웜 바이러스에 걸렸다. 컴퓨터의 수와 네트워크 상에서 서로 연결되어 있는 정보가 주어질 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 출력하는 프로그램을 작성하시오.

 

[입력]

첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어진다. 이어서 그 수만큼 한 줄에 한 쌍씩 네트워크 상에서 직접 연결되어 있는 컴퓨터의 번호 쌍이 주어진다.

 

[출력]

1번 컴퓨터가 웜 바이러스에 걸렸을 때, 1번 컴퓨터를 통해 웜 바이러스에 걸리게 되는 컴퓨터의 수를 첫째 줄에 출력한다.

 

[예제]

 


>문제 풀이

 문제의 이해도, 풀이도 어렵지 않은 문제

dfs와 bfs 둘 중 하나를 이용해서, 노드에 방문할 때마다 카운팅 해주면 되는 문제입니다.

(단, 1이 감염시킨 컴퓨터를 찾아야해서 1번 컴퓨터는 카운팅에서 빼주어야합니다.)

 

>전체 코드

 

import java.util.Scanner;

public class Main {
	static int N, M, cnt=0;
	static int visit[], mat[][];
	
	public static void main(String [] args) {
		int p1, p2;
		
		Scanner scan= new Scanner(System.in);
		
		N= scan.nextInt();
		M= scan.nextInt();
		
		visit= new int[N+1];
		mat= new int[N+1][N+1];
		for(int i=0; i<M; i++) {
			p1= scan.nextInt();
			p2= scan.nextInt();
			mat[p1][p2]=mat[p2][p1]=1;
		}
		
		dfs(1);
		System.out.println(cnt-1);
	}
	
	public static void dfs(int start) {
		visit[start]=1;
		cnt++;
		for(int i=1; i<=N; i++) {
			if(mat[start][i]==1 && visit[i]!=1) {
				dfs(i);
			}
		}
	}//dfs
	
}

www.acmicpc.net/problem/2606

 

2606번: 바이러스

첫째 줄에는 컴퓨터의 수가 주어진다. 컴퓨터의 수는 100 이하이고 각 컴퓨터에는 1번 부터 차례대로 번호가 매겨진다. 둘째 줄에는 네트워크 상에서 직접 연결되어 있는 컴퓨터 쌍의 수가 주어

www.acmicpc.net

 

1) DFS(Depth-First Search): 깊이 우선 탐색

: 스택 혹은 재귀함수를 이용한다.

:갈 수 있는 만큼 깊게 들어가고, 더이상 갈 수 없을 때 backtracking 한다.

 

1. 시작 노드를 스택에 넣고 방문 처리를 한다.

2. 스택의 최상단 노드에 연결된 노드 중 (보통) 번호가 낮은 노드를 스택에 넣고 방문처리

3. 최상단 노드의 인접노드가 없을 때 까지 반복하고, 인접노드가 없으면 스택에서 최상단 노드를 꺼낸다.

4. 위의 과정을 반복한다.

 

이런식으로 반복하게 된다.

 

<DFS 구현코드>

	public static void dfs(int start) {
		visit[start]=1;
		for(int i=1; i<=N; i++) {
			if(mat[start][i]==1 && visit[i]!=1) {
				dfs(i);
			}
		}
	}//dfs

인접행렬로 구현한 경우, 시간 복잡도는 O(n^2)

 

 

 

1) BFS(Breadth-First Search): 너비 우선 탐색

: 큐(Queue)를 사용.

: 현재 정점에서 갈 수 있는 노드들을 모두 큐에 넣는다.

 

1. 시작노드를 큐에 넣고, 방문처리

2. 큐에서 노드를 꺼내서, 해당 노드의 인접 노드 중 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리

3. 더이상 2번 과정을 수행할 수 없을 때까지 반복한다.

 

<BFS 구현코드>

	public static void bfs(int start) {
		int temp;
		Queue<Integer> queue= new LinkedList<Integer>();
		queue.offer(start);
		visit[start]=1;
		System.out.print(start+" ");
		
		while(!queue.isEmpty()) {
			temp= queue.poll();
			for(int i=1; i<=N; i++) {
				if(mat[temp][i]==1&&visit[i]!=1) {
					queue.offer(i);
					visit[i]= 1;
					System.out.print(i+" ");
				}
			}
		}
		
	}//bfs

 

 

 

유튜브 '동빈나' 님의 영상과 다른 글들을 보고 정리한 것입니다.

www.youtube.com/watch?v=7C9RgOcvkvo

 

+ Recent posts