www.acmicpc.net/problem/2178

문제

미로의 (1,1) 에서 (n,m)위치로 이동할 때 지나야 하는 최소의 칸 수를 구하는 문제이다.

BFS + DP를 이용하여 해결하였다.

 

해결 방안

1. 미로의 이동 가능 여부 관련 값은 maps[][]에 저장하였다.(1이면 이동 가능)
미로의 해당 칸으로 이동할 때 지나야하는 최소의 칸 수는 cnts[][]에 저장하였다.(0이면 한번도 이동 안한 곳)

2. 주어진 maps의 (1,1) 위치에서 (m,n)으로 BFS 방법으로 4방향을 탐색하고 DP를 이용하여 cnts에 지나야하는 최소 칸 수를 메모제이션하였다.

3. cnts[n][m]을 출력하면 도착 위치에 가기위해 지나야하는 최소의 칸을 출력한다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

// 미로 탐색
public class Main {

	static int n; // 세로
	static int m; // 가로
	static int[][] maps;
	static int[][] cnts; // 이동할 때 최소 칸수
	
	// 북 동 남 서
	static int[] dy = {-1, 0, 1, 0}; // 세로
	static int[] dx = {0, 1, 0, -1}; // 가로
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = br.readLine();
		n = Integer.parseInt(str.split(" ")[0]);
		m = Integer.parseInt(str.split(" ")[1]);
		
		maps = new int[n+1][m+1];
		cnts = new int[n+1][m+1];
		
		StringTokenizer st;
		for(int i=1; i<n+1; i++) {
			str = br.readLine();
			
			for(int j=1; j<m+1; j++) {
				maps[i][j] = Integer.parseInt(str.split("")[j-1]);
			}
		}
		
		// == 입력 끝
		
		bfs();

		// 도착위치(n,m) 의 최소 칸 수 
		System.out.println(cnts[n][m]);
	}
	
	static void bfs() {
		Queue<int[]> q = new LinkedList<>();
		
		// 1,1에서 시작
		q.offer(new int[]{1,1});
		cnts[1][1] = 1;
		
		// 세로, 가로
		int[] current = new int[2];
		int[] next = new int[2];
		while(!q.isEmpty()) {
			current = q.poll();
			
			// 4방향의 인접 위치
			for(int i=0; i<4; i++) {
				next[0] = current[0] + dy[i];
				next[1] = current[1] + dx[i];
				
				// 범위 안에 있고 이동할 수 있는 곳이고(map[][]==1) 방문한 적 없을 때(cnts[][]==0)
				if(next[0]>0 && next[1]>0 && next[0]<=n && next[1]<=m && maps[next[0]][next[1]]==1 && cnts[next[0]][next[1]]==0) {
					q.offer(new int[] {next[0], next[1]});
					cnts[next[0]][next[1]] = cnts[current[0]][current[1]]+1;
				}
			}
		}
	}

}

www.acmicpc.net/problem/2606

문제

입력의 첫째 줄이 그래프의 노드 갯수가 되고 둘째 줄이 간선의 갯수가 될 것이다.
또한 무방향 그래프가 될것이다. 

해결 방안

무방향 그래프를 ArrayList를 이용하여 구현하였다.

1번부터 시작하는 DFS 탐색을 하였으며 DFS의 depth를 바이러스 감염 시킨 컴퓨터 갯수로 하였다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

// 바이러스
public class Main {
	
	static int n; // v
	static int e; // edge
	static ArrayList<ArrayList<Integer>> graph;
	static boolean[] visited;
	static int cnt; // 결과

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		n = Integer.parseInt(br.readLine());
		e = Integer.parseInt(br.readLine());
		
		graph = new ArrayList<>();
		visited = new boolean[n+1];
		for(int i=0; i<=n; i++) {
			graph.add(new ArrayList<Integer>());
		}
		
		String str;
		int v1, v2;
		for(int i=0; i<e; i++) {
			str = br.readLine();
			v1 = Integer.parseInt(str.split(" ")[0]);
			v2 = Integer.parseInt(str.split(" ")[1]);
			
			graph.get(v1).add(v2);
			graph.get(v2).add(v1);
		}
		
		// ==== 입력 끝
		cnt = 0;
		dfs(1);
		
		System.out.println(cnt);
	}
	
	static void dfs(int start) {
		
		visited[start] = true;
		
		int y;
		for(int i=0; i<graph.get(start).size(); i++) {
			y = graph.get(start).get(i);
			
			if(!visited[y]) {
				visited[y] = true;
				cnt++;
				dfs(y);
			}
		}
	}

}

www.acmicpc.net/problem/10974

문제

해결 방안

모든 순열을 출력하는 기본적인 DFS 문제이다.

DFS + 백트래킹을 이용하여 구현하였다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

// 모든 순열
public class Main {

	static int n;
	static boolean[] visited;
	public static void main(String[] args) throws NumberFormatException, IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		n = Integer.parseInt(br.readLine());
		visited = new boolean[n+1];
		
		String sNum;
		for(int i=1; i<=n; i++) {
			visited[i] = true;
			sNum = Integer.toString(i);
			//System.out.println("sNum: " + sNum);
			dfs(sNum, i, 1);
			visited[i] = false;
		}
	}
	
	static void dfs(String sNum, int start, int depth) {
		if(depth>=n) {
			System.out.println(sNum);
		}
		else {
			for(int i=1; i<=n; i++) {
				if(!visited[i]) {
					visited[i] = true;
					sNum += " "+i;
					dfs(sNum, start, depth+1);
					sNum = sNum.substring(0, sNum.length()-2);
					visited[i] = false;
				}
			}
		}
	}

}

www.acmicpc.net/problem/1697

 

1697번: 숨바꼭질

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 ��

www.acmicpc.net

문제

해결 방안

time 배열의 index는 해당 위치이고 값은 해당 위치를 수빈이가 최소 몇 초를 나타낸다.
또한 수빈이는 3가지 방법으로 이동할 수 있으므로 3가지 방법으로 이동한 위치의 값이 k 일때까지 너비우선 탐색을 해주면 된다.

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;

// 숨바꼭질
// 수빈이 N 동생 K
// 수빈이는 걷거나 순간이동 가능
public class Main {

	static int n;
	static int k;
	static int[] time;
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = br.readLine();
		
		n = Integer.parseInt(str.split(" ")[0]);
		k = Integer.parseInt(str.split(" ")[1]);

		time = new int[100001];
		
		if(n==k)
			System.out.println(0);
		else
			bfs();
	}
	
	static void bfs() {
		Queue<Integer> q = new LinkedList<>();
		q.offer(n);
		
		int current = 0;
		
		int next = 0;
		while(!q.isEmpty()) {
			current = q.poll();

			for(int i=0; i<3; i++) {
				if(i==0) {
					next = current+1;
				}
				else if(i==1) {
					next = current-1;
				}
				else {
					next = 2*current;
				}
				
				if(next == k) {
					System.out.println(time[current]+1);
					return;
				}
				
				if(next>=0 && next<=100000 && time[next]==0) {
					q.offer(next);
					time[next] = time[current]+1;
				}
				
			}
		}
	}

}

www.acmicpc.net/problem/1766

문제

* 위상 정렬의 대표적인 문제이다.
(줄 세우기 문제에 약간 심화문제 우선순위 큐 사용)
yubh1017.tistory.com/manage/newpost/83?type=post&returnURL=https%3A%2F%2Fyubh1017.tistory.com%2F83

Queue를 이용한 위상정렬로 구현하였다. 

문제를 풀기전 간단하게 위상정렬에 대한 설명을 하자면

1. 위상 정렬은 그래프 정렬을 말한다.

2. 위상 정렬이 가능하려면 DAG(Directed Acyclic Graph, 방향성이 있으며 사이클이 없는 그래프) 여야 한다.

  • 두 노드 A,B 사이에 A->B 같은 관계가 성립되어야 하며
  • A->B 관계가 성립하면 B->A나 B->C->A와 같이 사이클이 생겨서는 안된다.

3. 자기 자신을 가리키는 간선의 갯수인 indegree 배열을 이용하여 구현할 수 있다.

4. 위상 정렬 과정

4.1) 위상 정렬을 위해 두개의 Queue를 만들어준다. 
q : indegree 값이 0인 노드들을 담기위한 Queue 
result: q에서 꺼내져 결과로 출력하기 위해 담는 결과 Queue

4.2) 처음 indegree 간선의 갯수가 0인 index를 q에 넣어준다.

4.3) q가 empty일 때까지 while문을 돈다.
q.poll()한 노드를 result에 넣어준다.(result.offer(노드))
그리고 해당 노드에 연결된 노드의 간선의 갯수들을 지워주면서 간선의 갯수가 0이 되면 q에 넣어준다.

4.4) result에 담긴 node가 위상정렬 순서가 된다.

 

* 자세한 개념은 References에서 참고할 수 있으며 코드를 보고 이해 바랍니다.

 

References

위상 정렬 개념 : bcp0109.tistory.com/entry/%EC%9C%84%EC%83%81%EC%A0%95%EB%A0%AC-Topological-Sort-Java?category=848939

 

해결 방안

문제 수 n이 정점(v)의 갯수가 되고 m은 간선(e)의 갯수가 될 것이다. 

따라서 이차원 ArrayList를 이용하여 단방향성 그래프를 생성해주고 가리킴을 받는 간선의 indegree[index]의 간선의 갯수를 증가해준다.

그리고 위상 정렬을 해준다.

여기서 3번 조건인 '가능하면 쉬운문제부터 풀어야한다.' 를 만족하기 위해 기존의 q에서 pq(우선순위큐)로 바꿔서 문제를 풀어준다. (문제는 난이도 순서로 출제되어 있기 때문에)

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;

// 문제집
// 우선순위 큐를 이용한 위상정렬2
// 가능하면 쉬운 문제를 먼저 풀어야 하며 난이도 순서로 문제가 출제되어있다. => 우선순위 큐를 사용
public class Main {

	static int n; // 문제 수
	static int m; // 조건(간선)
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = br.readLine();
		n = Integer.parseInt(str.split(" ")[0]);
		m = Integer.parseInt(str.split(" ")[1]);
		
		int[] indegree = new int[n+1];
		ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
		for(int i=0; i<n+1; i++) {
			graph.add(new ArrayList<Integer>());
		}
		
		int a,b;
		for(int i=0; i<m; i++) {
			str = br.readLine();
			a = Integer.parseInt(str.split(" ")[0]);
			b = Integer.parseInt(str.split(" ")[1]);
			
			graph.get(a).add(b);
			indegree[b]++;
		}
		// === 입력 끝
		
		topologicalSort(indegree, graph);
	}
	
	static void topologicalSort(int[] indegree, ArrayList<ArrayList<Integer>> graph) {
		PriorityQueue<Integer> pq = new PriorityQueue<>(); // 간선의 갯수가 0인것
		Queue<Integer> result = new LinkedList<>();
		
		for(int i=1; i<n+1; i++) {
			if(indegree[i]==0)
				pq.offer(i);
		}
		
		int node;
		while(!pq.isEmpty()) {
			node = pq.poll();
			result.offer(node);
			
			for(int i: graph.get(node)) {
				indegree[i]--;
				
				if(indegree[i]==0)
					pq.offer(i);
			}
		}
		
		while(!result.isEmpty()) {
			System.out.print(result.poll() + " ");
		}
	}

}

www.acmicpc.net/problem/2252

문제

* 위상 정렬의 대표적인 문제이다.

Queue를 이용한 위상정렬로 구현하였다. 

문제를 풀기전 간단하게 위상정렬에 대한 설명을 하자면

1. 위상 정렬은 그래프 정렬을 말한다.

2. 위상 정렬이 가능하려면 DAG(Directed Acyclic Graph, 방향성이 있으며 사이클이 없는 그래프) 여야 한다.

  • 두 노드 A,B 사이에 A->B 같은 관계가 성립되어야 하며
  • A->B 관계가 성립하면 B->A나 B->C->A와 같이 사이클이 생겨서는 안된다.

3. 자기 자신을 가리키는 간선의 갯수인 indegree 배열을 이용하여 구현할 수 있다.

4. 위상 정렬 과정

4.1) 위상 정렬을 위해 두개의 Queue를 만들어준다.
q : indegree 값이 0인 노드들을 담기위한 Queue
result: q에서 꺼내져 결과로 출력하기 위해 담는 결과 Queue

4.2) 처음 indegree 간선의 갯수가 0인 index를 q에 넣어준다.

4.3) q가 empty일 때까지 while문을 돈다.
q.poll()한 노드를 result에 넣어준다.(result.offer(노드))
그리고 해당 노드에 연결된 노드의 간선의 갯수들을 지워주면서 간선의 갯수가 0이 되면 q에 넣어준다.

4.4) result에 담긴 node가 위상정렬 순서가 된다.

 

* 자세한 개념은 References에서 참고할 수 있으며 코드를 보고 이해 바랍니다.

 

References

위상 정렬 개념 : bcp0109.tistory.com/entry/%EC%9C%84%EC%83%81%EC%A0%95%EB%A0%AC-Topological-Sort-Java?category=848939

 

해결 방안

사람 수 N이 정점(v)의 갯수가 되고 M은 간선(e)의 갯수가 될 것이다. 

따라서 이차원 ArrayList를 이용하여 단방향성 그래프를 생성해주고 가리킴을 받는 간선의 indegree[index]의 간선의 갯수를 증가해준다.

그리고 위상 정렬을 해준다.

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

// 줄 세우기
// 위상정렬 문제
// 단방향
public class Main {

	static int N;
	static int M;
	
	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = br.readLine();
		N = Integer.parseInt(str.split(" ")[0]); // 정점 갯수(사람 수)
		M = Integer.parseInt(str.split(" ")[1]); // 간선 갯수
		
		// 자기한테 온 간선의 갯수
		int[] indegree = new int[N+1];
		ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
		for(int i=0; i<N+1; i++) {
			graph.add(new ArrayList<Integer>());
		}
		
		int a,b;
		for(int i=0; i<M; i++) {
			str = br.readLine();
			a = Integer.parseInt(str.split(" ")[0]);
			b = Integer.parseInt(str.split(" ")[1]);
			
			graph.get(a).add(b);
			indegree[b]++;
		}
		// ==== 입력 끝
		
		topologicalSort(indegree, graph);
		
	}
	
	static void topologicalSort(int[] indegree, ArrayList<ArrayList<Integer>> graph) {
		Queue<Integer> q = new LinkedList<>(); // 간선의 갯수가 0인 정점이 들어갈 곳
		Queue<Integer> result = new LinkedList<>(); // 순서에 대한 결과가 들어갈 곳
		
		for(int i=1; i<=N; i++) {
			if(indegree[i]==0)
				q.offer(i);
		}
		
		int current;
		while(!q.isEmpty()) {
			current = q.poll();
			result.offer(current);
			
			for(int i : graph.get(current)) {
				indegree[i]--;
				
				if(indegree[i]==0)
					q.offer(i);
			}
		}
		
		while(!result.isEmpty()) {
			System.out.print(result.poll() + " ");
		}
	}

}

www.acmicpc.net/problem/7576

 

7576번: 토마토

첫 줄에는 상자의 크기를 나타내는 두 정수 M,N이 주어진다. M은 상자의 가로 칸의 수, N은 상자의 세로 칸의 수를 나타낸다. 단, 2 ≤ M,N ≤ 1,000 이다. 둘째 줄부터는 하나의 상자에 저장된 토마토�

www.acmicpc.net

문제

해결 방안

BFS를 기본으로하여 구현하였다. 

 

코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

// 토마토
// 며칠이 지나면 토마토가 다 익게되는지 최소 일수
public class Main {

	static int M; // 가로 (2~1000)
	static int N; // 세로
	static int[][] map; // NxM (1(익음),0(안익음),-1(없음))
	static boolean[][] visited;
	static Queue<int[]> loc = new LinkedList<int[]>(); // 익은 것들 위치
	static int[] dx = {0, 1, 0, -1}; // 북, 동, 남, 서
	static int[] dy = {-1, 0, 1, 0}; // 북, 동, 남, 서
	static int totalTomatoCnt = 0; // 총 토마토 갯수
	static int tomatoCnt = 0; // 익은 토마토 cnt
	static int result = 0; // 토마토가 모두 익는 최소 날짜
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String str = br.readLine();
		M = Integer.parseInt(str.split(" ")[0]);
		N = Integer.parseInt(str.split(" ")[1]);
		
		map = new int[N][M];
		visited = new boolean[N][M];
		
		StringTokenizer st;
		for(int i=0; i<N; i++) {
			str = br.readLine();
			st = new StringTokenizer(str);
			
			for(int j=0; j<M; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
				// 토마토 갯수
				if(map[i][j]==1 || map[i][j] == 0) {
					
					totalTomatoCnt++;
					// 익은 토마토를 
					if(map[i][j]==1) {
						tomatoCnt++;						
						loc.offer(new int[]{i,j});
						visited[i][j] = true;
					}
					
				}
			}
		}
		
		bfs();
		
		System.out.println(result);
	}
	
	static void bfs() {
		if(tomatoCnt == totalTomatoCnt) {
			return;
		}
		
		Queue<int[]> depthLoc = new LinkedList<>();
		int[] current = new int[2];
		int nx, ny;
		
		while(!loc.isEmpty()) {
			if(tomatoCnt == totalTomatoCnt) {
				break;
			}
			
			while(!loc.isEmpty()) {
				current = loc.poll();
				depthLoc.offer(current);
			}
			
			while(!depthLoc.isEmpty()) {
				current = depthLoc.poll();
				
				for(int i=0; i<4; i++) {
					nx = current[1] + dx[i];
					ny = current[0] + dy[i];
					if(nx<0 || nx>=M || ny<0 || ny>=N) {
						continue;
					}
					else {
						if(!visited[ny][nx] && map[ny][nx]==0) {
							visited[ny][nx] = true;
							map[ny][nx]=1;
							loc.offer(new int[] {ny, nx});
							tomatoCnt++;
						}
					}
				}
				
			}
			result++;
			
		}
		for(int i=0; i<N; i++) {
			for(int j=0; j<M; j++) {
				if(map[i][j]==0) {
					result = -1;
				}
			}
		}
		
	}

}

TCP란?

TCP는 네트워크 계층 중 전송 계층에서 사용하는 프로토콜 로서, 장치들 사이에 논리적인 접속을 성립(Establish) 하기 위하여 연결을 설정하여 신뢰성을 보장하는 연결형 서비스이다.

 

TCP 3-way HandkShake란?

TCP 통신을 이용하여 데이터를 전송하기 위해 네트워크 연결을 설정(Connection Establish)하는 과정이다.

TCP 3-way HandkShake 역할 및 과정

3-way HandkShake

[STEP 1]
Client는 Server에 접속을 요청하는 SYN 패킷을 보낸다. 이때 Client는 SYN을 보내고 SYN/ACK 응답을 기다리는 SYN_SENT 상태가 된다.

[STEP 2]
Server는 Client의 SYN 요청을 받고 Client에게 요청을 수락한다는 ACK와 SYN 가 설정된 패킷을 발송하고 A가 다시 ACK로 응답

[STEP 3]
Client가 Server에게 SYN+ACK를 받게되면 Server에게 응답을 확인했다는 ACK을 보낸다.
그 이후부터는 연결이 이루어지게되고 데이터가 오가게 된다.

위의 3단계를 거쳐 통신하는 것이 신뢰성 있는 연결을 맺어준다는 TCP의 3-Way HandShake방식이다. 

 

TCP 4-way HandkShake란?

TCP 통신 연결을 해제(Connection Termination)하는 과정이다.

TCP 4-way HandkShake 역할 및 과정

4-way HandkShake

[STEP 1]
App으로부터 close 신호를 받은 Client가 Server에게 연결을 종료하겠다는 FIN 플래그를 전송하게된다.

[STEP 2]
Server는 FIN 플래그를 받고 응답을 받았다는 의미로 ACK를 보낸다.
그리고 Server는 자신이 하던 데이터 통신이 끝날 때 까지 기다린다.(이 상태가 Time_Wait 상태)

[STEP 3]
Server가 통신이 끝났으면 연결이 종료되었다고 해당 Client한테 FIN 플래그를 전송한다.

[STEP 4]
Client는 응답을 받았다는 의미로 ACK를 보낸다.
이 때 Client는 패킷 유실이나 Routing 지연으로 인해 FIN패킷보다 늦게 도착하는 상황을 방지하기 위해 FIN 플래그를 받게되어도 일정시간동안 세션을 남겨두고 잉여 패킷을 기다린다.(이 때도, TIME_WAIT 상태)

 

참고 TCP Header 안의 플래그 정보

  • TCP Header에는 CONTROL BIT(플래그 비트, 6bit)가 존재하며, 각각의 bit는 “URG-ACK-PSH-RST-SYN-FIN”의 의미를 가진다.
    - 즉, 해당 위치의 bit가 1이면 해당 패킷이 어떠한 내용을 담고 있는 패킷인지를 나타낸다.
  • SYN(Synchronize Sequence Number)
    - 연결 설정 / 000010
    - Sequence Number를 랜덤으로 설정하여 세션을 연결하는 데 사용하며, 초기에 Sequence Number를 전송한다.
  • ACK(Acknowledgement)
    - 응답 확인 / 010000 1
    - 패킷을 받았다는 것을 의미한다. Acknowledgement Number 필드가 유효한지를 나타낸다.
    - 양단 프로세스가 쉬지 않고 데이터를 전송한다고 가정하면 최초 연결 설정 과정에서 전송되는 첫 번째 세그먼트를 제외한 모든 세그먼트의 ACK 비트는 1로 지정된다고 생각할 수 있다.
  • FIN(Finish)
    - 연결 해제 / 000001
    - 세션 연결을 종료시킬 때 사용되며, 더 이상 전송할 데이터가 없음을 의미한다.

References

gmlwjd9405.github.io/2018/09/19/tcp-connection.html

 

[Network] TCP 3-way handshaking과 4-way handshaking - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-22%ED%8E%B8-TCP-3-WayHandshake-4-WayHandshake

+ Recent posts