728x90

연결 요소의 개수

문제

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

입력

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

출력

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


문제풀이

 

2021.10.26 - [Programming/Algorithm] - [BFS] BOJ 2606번, 바이러스 (JAVA)

 

[BFS] BOJ 2606번, 바이러스 (JAVA)

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

snowoo.tistory.com

이 문제 와 유사한 문제로 백준 2026번 바이러스 라는 문제가 있다.

 

이 문제도 바이러스 문제와 마찬가지로 BFS, DFS, UNION FIND 로 해결할 수 있다.

 


SMALL

소스코드

import java.util.*;
import java.io.*;
class Main{
	static int n, m;
	static List<Integer>[] nodes;
	static boolean[] visited;
	static int ans;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		nodes = new ArrayList[n+1];
		visited = new boolean[n+1];
		for(int i=1;i<=n;i++)
			nodes[i] = new ArrayList<>();
		
		for(int i=0;i<m;i++)
		{
			st = new StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			//노드의 두 정점을 연결
			nodes[a].add(b);
			nodes[b].add(a);
		}
		
		for(int i=1;i<=n;i++)
		{
			if(!visited[i])
			{
				bfs(i);
				ans++;
			}
		}
		System.out.println(ans);
	}
	public static void bfs(int node)
	{
		Queue<Integer> q = new LinkedList<>();
		q.add(node);
		visited[node] = true;
		
		while(!q.isEmpty())
		{
			int now = q.poll();
			for(int next : nodes[now])
			{
				if(!visited[next])
				{
					visited[next] = true;
					q.add(next);
				}
			}
		}
	}
}
LIST
728x90

숨바꼭질

문제

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때 걷는다면 1초 후에 X-1 또는 X+1로 이동하게 된다. 순간이동을 하는 경우에는 1초 후에 2*X의 위치로 이동하게 된다.

수빈이와 동생의 위치가 주어졌을 때, 수빈이가 동생을 찾을 수 있는 가장 빠른 시간이 몇 초 후인지 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에 수빈이가 있는 위치 N과 동생이 있는 위치 K가 주어진다. N과 K는 정수이다.

출력

수빈이가 동생을 찾는 가장 빠른 시간을 출력한다.


문제풀이

 

수빈이의 위치 N 에서  -----------> 동생의 위치 K 로 가는 최소 시간을 구하는 문제.

 

처음 배열의 초기화를 int[] dist = new int[n or k] 이런식으로 해서 index 문제가 발생하였다.

 

문제를 읽어보면 배열이 앞으로 한칸, 뒤로 한칸, X2 위치 이동 이 가능하므로 배열 초기화를 최대범위로 해주어야 했다.

 


소스코드

import java.util.*;
import java.io.*;
class Main{
	static int n, k;
	static int[] dist = new int[100001];
	static int ans;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken());
		k = Integer.parseInt(st.nextToken());
		
		bfs(n);
		System.out.println(dist[k]);
	}
	
	public static void bfs(int start)
	{
		Queue<Integer> q = new LinkedList<>();
		q.add(start);
		
		while(!q.isEmpty())
		{
			int now = q.poll();
			if(now == k)
			{
				break;
			}
			int moves[] = {now-1, now+1, now*2};
			for(int i=0;i<3;i++)
			{
				int next = moves[i];
				if(next>=0 && next<=100000 && dist[next] == 0)
				{
					q.add(next);
					dist[next] = dist[now] + 1;
				}
			}
		}
	}
}
LIST
728x90

유기농 배추

문제

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

한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다. 예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 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)가 주어진다. 두 배추의 위치가 같은 경우는 없다.

출력

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


문제 풀이

 

import java.util.*;
import java.io.*;
class Main{
	static int n,m,k;
	static int[][] map;
	static boolean[][] visited;
	static int[] dx = {0, 0, -1, 1};
	static int[] dy = {1, -1, 0, 0};
	static int ans;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int tc = Integer.parseInt(br.readLine());
		StringBuilder sb = new StringBuilder();
		//testcase
		while(tc-- > 0)
		{
			StringTokenizer st = new StringTokenizer(br.readLine());
			//변수 초기화
			m = Integer.parseInt(st.nextToken());
			n = Integer.parseInt(st.nextToken());
			k = Integer.parseInt(st.nextToken());
			map = new int[n][m];
			visited = new boolean[n][m];
			ans = 0;
			
			//INPUT
			for(int i=0;i<k;i++)
			{
				st = new StringTokenizer(br.readLine());
				int a = Integer.parseInt(st.nextToken());
				int b = Integer.parseInt(st.nextToken());
				map[b][a] = 1;
			}
			
			for(int i=0;i<n;i++)
			{
				for(int j=0;j<m;j++)
				{
					//배추가 있고 방문하지 않았을 경우
					//bfs 탐색
					if(map[i][j] == 1 && !visited[i][j])
					{
						bfs(i,j);
						ans++;
					}
				}
			}
			sb.append(ans).append('\n');
		}
		System.out.println(sb);
	}
	public static void bfs(int y, int x)
	{
		Queue<Pos> q = new LinkedList<>();
		q.add(new Pos(y,x));
		visited[y][x] = true;
		while(!q.isEmpty())
		{
			Pos p = q.poll();
			for(int i=0;i<4;i++)
			{
				int ny = p.y + dy[i];
				int nx = p.x + dx[i];
				if(ny >=0 && nx >=0 && ny < n && nx < m)
				{
					if(!visited[ny][nx] && map[ny][nx] == 1)
					{
						q.add(new Pos(ny,nx));
						visited[ny][nx] = true;
					}
				}
			}
		}
	}
}
class Pos{
	int y,x;
	public Pos(int y, int x)
	{
		this.y = y;
		this.x = x;
	}
}
LIST
728x90

토마토

문제

철수의 토마토 농장에서는 토마토를 보관하는 큰 창고를 가지고 있다. 토마토는 아래의 그림과 같이 격자 모양 상자의 칸에 하나씩 넣어서 창고에 보관한다. 

창고에 보관되는 토마토들 중에는 잘 익은 것도 있지만, 아직 익지 않은 토마토들도 있을 수 있다. 보관 후 하루가 지나면, 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다. 하나의 토마토의 인접한 곳은 왼쪽, 오른쪽, 앞, 뒤 네 방향에 있는 토마토를 의미한다. 대각선 방향에 있는 토마토들에게는 영향을 주지 못하며, 토마토가 혼자 저절로 익는 경우는 없다고 가정한다. 철수는 창고에 보관된 토마토들이 며칠이 지나면 다 익게 되는지, 그 최소 일수를 알고 싶어 한다.

토마토를 창고에 보관하는 격자모양의 상자들의 크기와 익은 토마토들과 익지 않은 토마토들의 정보가 주어졌을 때, 며칠이 지나면 토마토들이 모두 익는지, 그 최소 일수를 구하는 프로그램을 작성하라. 단, 상자의 일부 칸에는 토마토가 들어있지 않을 수도 있다.

입력

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토들의 정보가 주어진다. 즉, 둘째 줄부터 N개의 줄에는 상자에 담긴 토마토의 정보가 주어진다. 하나의 줄에는 상자 가로줄에 들어있는 토마토의 상태가 M개의 정수로 주어진다. 정수 1은 익은 토마토, 정수 0은 익지 않은 토마토, 정수 -1은 토마토가 들어있지 않은 칸을 나타낸다.

토마토가 하나 이상 있는 경우만 입력으로 주어진다.

출력

여러분은 토마토가 모두 익을 때까지의 최소 날짜를 출력해야 한다. 만약, 저장될 때부터 모든 토마토가 익어있는 상태이면 0을 출력해야 하고, 토마토가 모두 익지는 못하는 상황이면 -1을 출력해야 한다.


상자 안에 토마토가 하루가 지날때 인접한 토마토가 익어가는데 총 며칠이 걸리는지 구하는 문제

실제 토마토가 익는데 걸리는 날짜를 구하는데에는 어렵지 않지만

예외사항으로 처음부터 모두 익은 형태의 입력이 주어지거나 

모두 익지 못하는 상황이 주어졌을때 0, -1을 출력하는 경우를 고려해서 풀어주었다.


소스코드

import java.util.*;
import java.io.*;
class Main{
	static int n,m;
	static int[][] map;
	static boolean[][] visited;
	static int[] dx = {0, 0, -1, 1};
	static int[] dy = {1, -1, 0, 0};
	static int max;
	static int[] ans = new int[2];
	static Queue<Pos> q = new LinkedList<>();
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		m = Integer.parseInt(st.nextToken());
		n = Integer.parseInt(st.nextToken());
		map = new int[n][m];
		visited = new boolean[n][m];
		for(int i=0;i<n;i++)
		{
			st = new StringTokenizer(br.readLine());
			for(int j=0;j<m;j++)
			{
				map[i][j] = Integer.parseInt(st.nextToken());
				if(map[i][j] == 1)
				{
					//익은 토마토를 전부 큐에 넣어서 bfs를 이용해서
					//익은 토마토를 확산 시킨다.
					q.add(new Pos(i,j));
					visited[i][j] = true;
				}
			}
		}
		
		bfs();
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<m;j++)
			{
				if(map[i][j] == 0)
					ans[1]++;
				else if(map[i][j] == 2)
					ans[0]++;
				max = Math.max(max, map[i][j]);
			}
		}
		if(ans[1] > 0)
			System.out.println(-1);
		else if(ans[0] == 0)
			System.out.println(0);
		else
			System.out.println(max-1);
		
	}
	public static void bfs()
	{
		while(!q.isEmpty())
		{
			Pos p = q.poll();
			for(int i=0;i<4;i++)
			{
				int ny = p.y + dy[i];
				int nx = p.x + dx[i];
				if(ny >= 0 && nx >= 0 && nx < m && ny < n)
				{
					if(!visited[ny][nx] && map[ny][nx] == 0)
					{
						//날짜를 저장하기 위해 +1 하는 방법을 이용하였고
						//결과값에서 1을 뺴주어야 한다.
						map[ny][nx] = map[p.y][p.x] + 1;
						visited[ny][nx] = true;
						q.add(new Pos(ny,nx));
					}
				}
			}
		}
	}
}
class Pos{
	int y,x;
	public Pos(int y, int x)
	{
		this.y = y;
		this.x = x;
	}
}

 

LIST
728x90

바이러스

문제

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

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

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

입력

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

출력

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


이 문제의 경우에는 내가 아는 해결방법은 세가지가 있다.

1. DFS 깊이 우선 탐색

2. BFS 너비 우선 탐색

3. Union Find 

 

세가지 풀이 방법에 대해 주석과 함께 소스코드를 올려보았다.


1. DFS

import java.util.*;
import java.io.*;
class Main{
	static int n,m;
	static ArrayList<Integer>[] tree;
	static boolean[] visited;
	static int ans;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		n = Integer.parseInt(br.readLine());
		m = Integer.parseInt(br.readLine());
		visited = new boolean[n+1];
		tree = new ArrayList[n+1];
		for(int i=1;i<=n;i++)
			tree[i] = new ArrayList<>();
		
		for(int i=0;i<m;i++)
		{
			StringTokenizer st = new StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			tree[a].add(b);
			tree[b].add(a);
		}
		
		dfs(1);
        //문제에서는 1 때문에 감염된 노드의 수를 물어봤으므로 감염된 전체 노드의 개수에서 1을
        //빼준다.
		System.out.println(ans-1);
	}
	public static void dfs(int now)
	{
        //방문했으면 종료
		if(visited[now]) return;
		visited[now] = true;
		ans++;
		for(int next : tree[now])
		{
        	//연결된 노드 중에 방문하지 않았으면 방문
			if(!visited[next])
			{
				dfs(next);
			}
		}
	}
}

 

2. BFS

import java.util.*;
import java.io.*;
class Main{
	static int n,m;
	//입력 배열
	static ArrayList<Integer>[] tree;
	static boolean[] visited;
	static int ans;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		n = Integer.parseInt(br.readLine());
		m = Integer.parseInt(br.readLine());
		visited = new boolean[n+1];
		tree = new ArrayList[n+1];
		for(int i=1;i<=n;i++)
			tree[i] = new ArrayList<>();
		
		for(int i=0;i<m;i++)
		{
			StringTokenizer st = new StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			tree[a].add(b);
			tree[b].add(a);
		}
		
		bfs(1);
		System.out.println(ans);
	}
	public static void bfs(int start)
	{
		Queue<Integer> q = new LinkedList<>();
		q.add(start);
		visited[start] = true;
		while(!q.isEmpty())
		{
			int now = q.poll();
			for(int next : tree[now])
			{
				if(!visited[next])
				{
					visited[next] = true;
					q.add(next);
					ans++;
				}
			}
		}
	}
}

3. Union-Find

import java.util.*;
import java.io.*;
class Main{
	static int n,m;
	static int[] parent;
	static int ans;
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		n = Integer.parseInt(br.readLine());
		m = Integer.parseInt(br.readLine());
		parent = new int[n+1];
		for(int i=1;i<=n;i++)
		{
			parent[i] = i;
		}
		
		for(int i=0;i<m;i++)
		{
			StringTokenizer st = new StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			union(a,b);
		}
		for(int i=1;i<=n;i++)
		{
			if(find(i) == 1)
				ans++;
		}
		System.out.print(ans-1);
	}
	
	public static int find(int x)
	{
		if(parent[x] == x) return x;
		return parent[x] = find(parent[x]);
	}
	
	public static void union(int a,int b)
	{
		a = find(a);
		b = find(b);
		if(b>a)parent[b] = a;
		else parent[a] = b;
	}
}
LIST
728x90

미로 탐색

문제

N×M크기의 배열로 표현되는 미로가 있다.

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

미로에서 1은 이동할 수 있는 칸을 나타내고, 0은 이동할 수 없는 칸을 나타낸다. 이러한 미로가 주어졌을 때, (1, 1)에서 출발하여 (N, M)의 위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 프로그램을 작성하시오. 한 칸에서 다른 칸으로 이동할 때, 서로 인접한 칸으로만 이동할 수 있다.

위의 예에서는 15칸을 지나야 (N, M)의 위치로 이동할 수 있다. 칸을 셀 때에는 시작 위치와 도착 위치도 포함한다.

입력

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

출력

첫째 줄에 지나야 하는 최소의 칸 수를 출력한다. 항상 도착위치로 이동할 수 있는 경우만 입력으로 주어진다.


1. 주어진 배열에서 특정 조건을 만족할때 이동하는 경로의 최소값을 구하는 문제 유형

 

문제에서는 배열이 이동할 수 있는 경우는 값이 1일 때 이다

 

이러한 문제유형은

int[] dx = {1, -1, 0, 0};

int[] dy = {0, 0, 1, -1};

for(int i=0;i<4;i++)

다음과 같이 4방향 배열을 만들고 각각의 경우를 범위와 값을 비교하면서 탐색해 주면된다.

 

그리고 새로운 dist 배열에 이동할때마다 증가하는 값을 저장해서 dist[n-1][m-1] 위치의 값을 출력해 주었다.

 


import java.util.*;
import java.io.*;
class Main{
	static int n, m;
	static int result;
	static int[][] arr;
	static int[][] dist;
	
	static int[] dx = {1, -1, 0, 0};
	static int[] dy = {0, 0, 1, -1};
	static StringBuilder sb = new StringBuilder();
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		
		arr = new int[n][m];
		dist = new int[n][m];
		for(int i=0;i<n;i++)
		{
			String s = br.readLine();
			for(int j=0;j<m;j++)
			{
				int k = s.charAt(i) - '0';
				arr[i][j] = k;
			}
		}
		
		bfs(0,0);
		
		System.out.println(dist[n-1][m-1]);
	}
	public static void bfs(int y, int x)
	{
		Queue<Pos> q = new LinkedList<>();
		q.add(new Pos(y,x));
		dist[y][x] = 1;
		
		while(!q.isEmpty())
		{
			Pos now = q. poll();
			for(int i=0;i<4;i++)
			{
				int ny = now.y + dy[i];
				int nx = now.x + dx[i];
				
				if(nx >= 0 && ny >=0 && nx < m && ny < n)
				{
					if(dist[ny][nx] == 0 && arr[ny][nx] == 1)
					{
						dist[ny][nx] = dist[now.y][now.x] + 1;
						q.add(new Pos(ny,nx));
					}
				}
			}
		}
	}
}
class Pos{
	int y, x;
	public Pos(int y, int x)
	{
		this.y = y;
		this.x = x;
	}
}
LIST
728x90

DFS와 BFS 

문제

그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.

입력

첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다.

출력

첫째 줄에 DFS를 수행한 결과를, 그 다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.


이 문제는 DFS와 BFS를 사용할 수 있는지 확인하는 문제입니다.

 

//문제에서 숫자의 범위가 1부터 시작해서 체크 배열을 n+1로 설정

boolean[] visited = new boolean[n+1];

 

// list 배열의 의미는 list[i] : i 와 연결된 모든 노드

List<Integer>[] list


import java.util.*;
import java.io.*;
class Main{
	static int n, m, start;
	static boolean[] visited;
	static List<Integer>[] list;
	static List<Integer> dfs_result = new ArrayList<>();
	static StringBuilder sb = new StringBuilder();
	public static void main(String[] args) throws IOException{
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		n = Integer.parseInt(st.nextToken());
		m = Integer.parseInt(st.nextToken());
		start = Integer.parseInt(st.nextToken());

		list = new ArrayList[n+1];
		for(int i=1;i<=n;i++)
		{
			list[i] = new ArrayList<>();
		}
		
		for(int i=0;i<m;i++)
		{
			st = new StringTokenizer(br.readLine());
			int a = Integer.parseInt(st.nextToken());
			int b = Integer.parseInt(st.nextToken());
			
			list[a].add(b);
			list[b].add(a);
		}
		for(int i=1;i<=n;i++)
			Collections.sort(list[i]);
		
		visited = new boolean[n+1];
		dfs(start);
		sb.append('\n');
		visited = new boolean[n+1];
		bfs(start);
		
		System.out.println(sb);
	}
	
	static void dfs(int now)
	{
		if(visited[now]) return;
		visited[now] = true;
		sb.append(now).append(" ");
		for(int next : list[now])
		{
			if(!visited[next])
				dfs(next);
		}
	}
	
	static void bfs(int start)
	{
		Queue<Integer> q = new LinkedList<>();
		q.add(start);
		visited[start] = true;
		
		while(!q.isEmpty())
		{
			int now = q.poll();
			sb.append(now).append(" ");
			for(int next : list[now])
			{
				if(!visited[next])
				{
					visited[next] = true;
					q.add(next);
				}
			}
		}
	}
}
LIST
728x90

문제 설명

 

프렌즈대학교 컴퓨터공학과 조교인 제이지는 네오 학과장님의 지시로, 학생들의 인적사항을 정리하는 업무를 담당하게 되었다.

그의 학부 시절 프로그래밍 경험을 되살려, 모든 인적사항을 데이터베이스에 넣기로 하였고, 이를 위해 정리를 하던 중에 후보키(Candidate Key)에 대한 고민이 필요하게 되었다.

후보키에 대한 내용이 잘 기억나지 않던 제이지는, 정확한 내용을 파악하기 위해 데이터베이스 관련 서적을 확인하여 아래와 같은 내용을 확인하였다.

  • 관계 데이터베이스에서 릴레이션(Relation)의 튜플(Tuple)을 유일하게 식별할 수 있는 속성(Attribute) 또는 속성의 집합 중, 다음 두 성질을 만족하는 것을 후보 키(Candidate Key)라고 한다.
    • 유일성(uniqueness) : 릴레이션에 있는 모든 튜플에 대해 유일하게 식별되어야 한다.
    • 최소성(minimality) : 유일성을 가진 키를 구성하는 속성(Attribute) 중 하나라도 제외하는 경우 유일성이 깨지는 것을 의미한다. 즉, 릴레이션의 모든 튜플을 유일하게 식별하는 데 꼭 필요한 속성들로만 구성되어야 한다.

제이지를 위해, 아래와 같은 학생들의 인적사항이 주어졌을 때, 후보 키의 최대 개수를 구하라.

위의 예를 설명하면, 학생의 인적사항 릴레이션에서 모든 학생은 각자 유일한 "학번"을 가지고 있다. 따라서 "학번"은 릴레이션의 후보 키가 될 수 있다.
그다음 "이름"에 대해서는 같은 이름("apeach")을 사용하는 학생이 있기 때문에, "이름"은 후보 키가 될 수 없다. 그러나, 만약 ["이름", "전공"]을 함께 사용한다면 릴레이션의 모든 튜플을 유일하게 식별 가능하므로 후보 키가 될 수 있게 된다.
물론 ["이름", "전공", "학년"]을 함께 사용해도 릴레이션의 모든 튜플을 유일하게 식별할 수 있지만, 최소성을 만족하지 못하기 때문에 후보 키가 될 수 없다.
따라서, 위의 학생 인적사항의 후보키는 "학번", ["이름", "전공"] 두 개가 된다.

릴레이션을 나타내는 문자열 배열 relation이 매개변수로 주어질 때, 이 릴레이션에서 후보 키의 개수를 return 하도록 solution 함수를 완성하라.

 

제한사항

  • relation은 2차원 문자열 배열이다.
  • relation의 컬럼(column)의 길이는 1 이상 8 이하이며, 각각의 컬럼은 릴레이션의 속성을 나타낸다.
  • relation의 로우(row)의 길이는 1 이상 20 이하이며, 각각의 로우는 릴레이션의 튜플을 나타낸다.
  • relation의 모든 문자열의 길이는 1 이상 8 이하이며, 알파벳 소문자와 숫자로만 이루어져 있다.
  • relation의 모든 튜플은 유일하게 식별 가능하다.(즉, 중복되는 튜플은 없다.)

입출력

relation result
[["100","ryan","music","2"],
["200","apeach","math","2"],
["300","tube","computer","3"],
["400","con","computer","4"],
["500","muzi","music","3"],
["600","apeach","music","2"]]
2

문제 풀이

1. 깊이우선탐색을 통해서 모든 튜플 조합을 만들어본다.

2. 해당 조합이 유일성, 최소성을 만족하는지 확인하고 개수를 증가시킨다.

---------------------------------------------------------------------------------

1. 조합 개수가 1 개부터 relation.size() 만큼 일때까지 index 조합을 구한다.

2. 조합을 구했으면 유일성과, 최소성을 만족하는지 확인하고 만족할 경우 ans++, result에 집어넣는다.


소스 코드

 

#include <vector>
#include <string>
#include <algorithm>
#include <map>
using namespace std;
bool selected[8];
vector<vector<int>> result;
bool check_unique(vector<vector<string>> relation, vector<int> index)
{
    map<string, int> tmp;
    for(int i=0;i<relation.size();i++)
    {
        string str= "";
        for(int j=0;j<index.size();j++)
        {
            str += relation[i][index[j]];
        }
        tmp[str]++;
    }
    for(auto value : tmp)
    {
        if(value.second > 1)
            return false;
    }
    return true;
}
bool check_minimal(vector<vector<string>> relation, vector<int> index)
{
    vector<int> tmp;
    if(index.size() == 1)
        return true;
    for(int i=0;i<result.size();i++)
    {
        bool flag = false;
        for(int j=0;j<result[i].size();j++)
        {
            int value = result[i][j];
            if(find(index.begin(), index.end(), value) == index.end())
            {
                flag = true;
                break;
            }
        }
        if(!flag)
        {
            return false;
        }
    }
    return true;
}
void dfs(int idx, int depth, int cnt, vector<vector<string>> relation, int& ans)
{
    if(depth == cnt)
    {
        vector<int> tmp;
        for(int i=0;i<relation[0].size();i++)
        {
            if(selected[i])
                tmp.push_back(i);
        }
        if(check_unique(relation, tmp) && check_minimal(relation, tmp))
        {
            result.push_back(tmp);
            ans++;
        }
        return;
    }
    for(int i=idx;i<relation[0].size();i++)
    {
        if(selected[i]) continue;
        selected[i] = true;
        dfs(i, depth+1, cnt, relation, ans);
        selected[i] = false;
    }
}
int solution(vector<vector<string>> relation)
{
    int ans = 0;
    for(int i=1;i<=relation[0].size();i++)
        dfs(0, 0, i, relation, ans);
    return ans;
}
LIST

+ Recent posts