Per ardua ad astra !
I'm On My Way
Per ardua ad astra !
전체 방문자
오늘
어제
  • 분류 전체보기 (126)
    • Algorithm (50)
      • 백준 (30)
      • SWEA (3)
      • JUNGOL (3)
      • Programmers (5)
      • LeetCode (2)
    • 안드로이드 개발 (6)
      • Java로 개발 (0)
      • Kotlin으로 개발 (3)
    • Spring (41)
      • Spring기본 (17)
      • JPA기본 (15)
      • JPA활용 SpringBoot 기본 (9)
      • API 개발 기본 (0)
    • 네트워크 (3)
    • 운영체제 (0)
    • Life (3)
      • 책 (0)
      • 자기계발 (1)
      • 일상 (2)
    • 노마드코더 (3)
      • python으로 웹 스크래퍼 만들기 (3)
    • 프로그래밍 언어 (17)
      • Java 기본 (2)
      • 코틀린 기본 (15)

블로그 메뉴

  • 홈
  • 방명록

인기 글

hELLO · Designed By 정상우.
Per ardua ad astra !

I'm On My Way

Algorithm/백준

BaekJoon 9466. 텀 프로젝트 (Java)(DFS)(골드4)

2020. 8. 27. 20:58

문제출저 : https://www.acmicpc.net/problem/9466

 

9466번: 텀 프로젝트

문제 이번 가을학기에 '문제 해결' 강의를 신청한 학생들은 텀 프로젝트를 수행해야 한다. 프로젝트 팀원 수에는 제한이 없다. 심지어 모든 학생들이 동일한 팀의 팀원인 경우와 같이 한 팀만

www.acmicpc.net

문제 조건

- 텀프로젝트를 해야 한다. 학생은 자신을 포함한 단 한명을 선택해서 팀을 꾸릴 수 있다.

- 자세한 설명은 문제 참조.

- 그래프가 cycle을 이뤄야 팀 생성 가능. 답은 팀에 속하지 못한 학생들의 수 이기 때문에

   N - (cycle을 이룬 학생 수) 가 됨.

 

풀이 과정 

 

cycle을 찾아서 그에 해당하는 학생수를 찾으면 된다고 생각.

 

코드 구현

(1) 첫번째 시도

package 백준;

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

public class Ex_9466 {
	// 텀 프로젝트(골드4) union-find 문제
	private static boolean[] visited;
	private static Queue<Integer> queue = new LinkedList<Integer>();
	private static int answer = 0;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		int T = Integer.parseInt(br.readLine());
		for (int i = 1; i <= T; i++) {
			int N = Integer.parseInt(br.readLine());

			// 인접 리스트
			int adj[] = new int[N + 1];
			StringTokenizer st = new StringTokenizer(br.readLine(), " ");

			for (int node = 1; node < N + 1; node++) {
				adj[node] = Integer.parseInt(st.nextToken());
			}

			visited = new boolean[N + 1];

			for (int node = 1; node < N + 1; node++) {
				dfs(adj, node, 1);
			}
			// 정답 출력
			bw.write(N - answer + "\n");

			answer = 0;
		}
		bw.flush();
		bw.close();
		br.close();
	}

	private static void dfs(int[] adj, int node, int depth) {
		if (visited[node]) {
			queue.clear();
			return;
		}

		visited[node] = true;

		if (adj[node] == node) {
			answer++;
			queue.clear();
			return;
		}

		if (queue.contains(adj[node])) {
			int temp = queue.poll();
			while (temp != adj[node]) {
				depth--;
				temp = queue.poll();
			}
			answer += depth;
			queue.clear();
			return;
		}

		queue.offer(node);

		dfs(adj, adj[node], depth + 1);

	}
}

 

 cycle을 찾을 때,

   1) 자기 자신을 선택해서 혼자 팀을 이룬경우  

   2) 팀원들이 순환을 이루도록 선택한 경우. -> 이를 확인하기 위해 Queue를 생성하고 학생 수를 샜다.

 

   => 시간초과 !! 큐로 순환을 찾는 것이 비효율 적임을 알게됨.

 

 

(2) 두번째 시도

package 백준;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.StringTokenizer;

public class Ex_9466_3 {
	// 텀 프로젝트(골드4) union-find 문제
	private static int[] adj;
	private static boolean[] visited;
	private static boolean[] finished;
	private static int answer = 0;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

		int T = Integer.parseInt(br.readLine());
		for (int i = 1; i <= T; i++) {
			int N = Integer.parseInt(br.readLine());

			// 인접 리스트
			adj = new int[N + 1];
			StringTokenizer st = new StringTokenizer(br.readLine(), " ");

			for (int node = 1; node < N + 1; node++) {
				adj[node] = Integer.parseInt(st.nextToken());
			}

			visited = new boolean[N + 1];
			finished = new boolean[N + 1];

			for (int node = 1; node < N + 1; node++) {
				dfs(node);
			}
			// 정답 출력
			bw.write(N - answer + "\n");

			answer = 0;
		}
		bw.flush();
		bw.close();
		br.close();
	}

	private static void dfs(int node) {
		if (visited[node])
			return;
		visited[node] = true;
		int nextNode = adj[node];

		if (visited[nextNode] != true) {
			dfs(nextNode);
		} else {
			if (finished[nextNode] != true) {
				answer++;
				for (int i = nextNode; i != node; i = adj[i]) {
					answer++;
				}
			}
		}
		finished[node] = true;
	}
}

 

두 번째 시도

 

  2개의 배열 필요 

   1) boolean visited[] : 메소드에 처음들어왔든, 혹은 재귀로 불려서 들어왔든 한버니라도 호출된 node인지

                               방문처리를 해주는 배열문

   2) boolean finished[] : visited와 달리, main의 for문으로 dfs를 수행한 노드만을 체킹해주는 배열문

 

  dfs 메소드 살펴보기

private static void dfs(int node) {
		if (visited[node])
			return;
		visited[node] = true;
		int nextNode = adj[node];

		if (!visited[nextNode]) {
			dfs(nextNode);
		} else {
			if (!finished[nextNode]) {
				answer++;
				for (int i = nextNode; i != node; i = adj[i]) {
					answer++;
				}
			}
		}
		finished[node] = true;
	}
}

if(!visited[nextNode]) 부터 보면

다음노드가 방문한 곳이 아니면 dfs재귀로 차례차례 방문한다. 

 

만약 방문한 곳이라면 finished[]배열을 통해서 main for문의 dfs함수를 통해 하나씩 지나간 노드인지 확인한다.

이미 지나간 노드일 경우(finisthed[node]가 true)에는 중북해서 사이클을 카운트할 필요가 없기 때문이다. 

 

지나간 노드가 아닐경우

for문에서 adj[i]로 계속 갱신하면서 첫 노드인 node와 같은 값이 몇번째에 나오는지 카운트 해준다. 

카운트 해준 값이 싸이클을 이루는 노드의 개수 인 것이다.

 

한번의 dfs가 끝나면 끝난 노드의 finished[node]를 true로 체크 해준다. 

 

 

알게된 점

 

Union-Find는 그래프가 완벽한 싸이클을 이루지 않고 연결만 되어 있더라고 사용할 수 있었지만,

이 경우에는 그래프들이 단방향이고 완벽한 싸이클을 이뤘을 때 체크해주어야 한다. 

 

이를 알기 위해서는 2개의 배열

 

visited[]: 방문처리 배열

finished[]: main의 for문에 dfs(node)를 사용했을 떄 들어온 노드 체크처리 배열

 

을 알아야 함을 깨달았다. 

    'Algorithm/백준' 카테고리의 다른 글
    • BaekJoon 7576. 토마토(Java)(BFS)(실버1)
    • BaekJoon 1937. 욕심쟁이 판다(Java)(DFS,DP)(골드3)
    • BaekJoon 1074. Z (Java)(분할 정복)(실버1)
    • BaekJoon 17472. 다리 만들기 2 (Java)(BFS, DFS, UNION-FIND)(골드3)
    Per ardua ad astra !
    Per ardua ad astra !
    개발자 지망생이며 열심히 공부하고 기억하기 위한 블로그입니다.

    티스토리툴바