https://www.acmicpc.net/problem/1325

해설

이 문제는 전형적인 그래프에 BFS를 돌리는 문제입니다.

다만, 문제 조건에 따라 a -> b 로 그래프의 방향을 두는것이 나닌 b -> a 로 그래프 방향을 추가하여야 신뢰할 수 있는 컴퓨터 간의 관계를 올바르게 사용할 수 있습니다.

 

또한 이 문제 같은경우 JAVA로 BFS를 풀이하면, 시간초과가 난다는 리뷰들이 존재하는데, 저 또한 계속해서 해당 문제에 대해 시간초과가 존재하지만, BufferedReader, static 과 같은 해결방법을 사용하려 해도 해결되지 않아 일단은 이 상태로 제출하였으니 참고 부탁드립니다.

 

시간초과를 해결한 방법은, A->B로 갈떄마다 cache[B] += 1 로 하여 B의 자식노드를 갱신시킨 것 입니다.

처음에 시간초과가 난 방식은, A->B 로 가면, 저는 B->A로 가도록하여 B의 자식노드 개수가 몇개인지 Counting 했습니다.

하지만 그 대신에 A에서 B노드로 올라가도록 처리하면 더 빠르게 처리합니다.

명확한 이유는 모르겠지만, DFS 입장에서 자식 노드가 분기되면서 모든 자식을 방문하는 것보다는 하나의 자식노드가 각각 부모노드로 올라가는게 DFS의 깊이가 더 작아지리라는 것을 생각할 수 있습니다.

명확한 이유를 찾지 못하여서 이유를 만들어서 찾아낸 것 같습니다. 알 수가 없네요.

코드

정답코드1입니다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
public class Main {
static int N, M, S, P, K, A, B, X, L, R;
static int answer = 0;
static ArrayList<Integer>[] graph = new ArrayList[10001];
static PriorityQueue<Integer> pq = new PriorityQueue<>();
static ArrayList<Integer> answerArr = new ArrayList<>();
static int[] cache = new int[10001];
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());
Arrays.fill(cache, -1);
for(int i=0;i<N; i++) {
graph[i] = new ArrayList<Integer>();
}
for(int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine());
int A = Integer.parseInt(st.nextToken()) - 1;
int B = Integer.parseInt(st.nextToken()) - 1;
graph[A].add(B);
}
for(int i=0;i<N; i++) {
BFS(i, new boolean[N]);
}
for(int i=0;i<N; i++) {
answer = Math.max(answer, cache[i]);
}
for(int i=0;i<N; i++) {
if(answer == cache[i]) sb.append(i + 1).append(" ");
}
System.out.println(sb);
}
static void DFS(int now, boolean[] visited) {
visited[now] = true;
int ret = 0;
for(int connected = 0; connected < graph[now].size(); connected++) {
int nextNode = graph[now].get(connected);
if(visited[nextNode] == true) continue;
cache[nextNode]++;
DFS(nextNode, visited);
}
return ;
}
static void BFS(int now, boolean[] visited) {
Queue<Integer> q = new LinkedList<>();
q.offer(now);
visited[now] = true;
while(!q.isEmpty()) {
now = q.poll();
for(int connected = 0; connected < graph[now].size(); connected++) {
int nextNode = graph[now].get(connected);
if(visited[nextNode] == true) continue;
visited[nextNode] = true;
cache[nextNode]++;
q.offer(nextNode);
}
}
}
}

 

시간초과 나는 BFS코드입니다.

import java.util.*;
public class Main {
public static int N,M;
public static ArrayList<ArrayList<Node>> nodes = new ArrayList<>();
public static int[] answer;
public static boolean[] visited;
public static int cnt=0;
public static int max = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N=sc.nextInt();
M=sc.nextInt();
answer = new int[N+1];
for(int i=0;i<=N;i++) {
nodes.add(new ArrayList<>());
}
for(int i=0;i<M;i++) {
int a = sc.nextInt();
int b = sc.nextInt();
nodes.get(b).add(new Node(a));
}
for(int i=1;i<=N;i++) {
visited = new boolean[N+1];
cnt = 0;
BFS(i);
answer[i] = cnt;
max = Math.max(max, cnt);
}
for(int i=1;i<=N;i++) {
if(answer[i] == max)
System.out.print(i+" ");
}
}
public static void BFS(int start) {
Queue<Integer> q = new LinkedList<>();
q.offer(start);
visited[start] = true;
while(!q.isEmpty()) {
int startNode = q.poll();
for(int i=0;i<nodes.get(startNode).size();i++) {
int endNode = nodes.get(startNode).get(i).nodeB;
if(visited[endNode] == false) {
q.offer(endNode);
visited[endNode] = true;
cnt+=1;
}
}
}
}
}
class Node {
int nodeB;
public Node(int nodeB) {
this.nodeB = nodeB;
}
}

 

DFS + 메모이제이션을 사용할시 2%에서 틀립니다.

아래의 경우 메모이제이션을 적용할 수 있지 않을까 해서 적용했는데, 최적 부분구조가 성립하지 않는 것으로 보입니다.

이유는, A->B 단방향그래프가 B->A로도 가능한 경우가 존재하기 때문인 것 같습니다.

만약 메모이제이션을 뺼 경우, 시간초과가 발생하고 틀리지는 않습니다.

package Main;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int N, M, S, P, K, A, B, X, L, R;
static int answer = 0;
static ArrayList<Integer>[] graph = new ArrayList[10001];
static PriorityQueue<Integer> pq = new PriorityQueue<>();
static ArrayList<Integer> answerArr = new ArrayList<>();
static int[] cache = new int[10001];
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());
Arrays.fill(cache, -1);
for(int i=0;i<N; i++) {
graph[i] = new ArrayList<Integer>();
}
for(int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine());
int A = Integer.parseInt(st.nextToken()) - 1;
int B = Integer.parseInt(st.nextToken()) - 1;
graph[B].add(A);
}
for(int i=0;i<N; i++) {
int temp = DFS(i, new boolean[N]);
if(answer < temp) {
answer = temp;
answerArr.clear();
answerArr.add(i);
}else if(answer == temp) {
answerArr.add(i);
}
}
for(int v : answerArr) {
sb.append(v+1).append(" ");
}
System.out.println(sb);
}
static int DFS(int now, boolean[] visited) {
if(cache[now] != -1) return cache[now];
visited[now] = true;
int ret = 0;
for(int connected = 0; connected < graph[now].size(); connected++) {
int nextNode = graph[now].get(connected);
if(visited[nextNode] == true) continue;
ret += DFS(nextNode, visited) + 1;
}
return cache[now] = ret;
}
}

 

메모이제이션을 제거하고 DFS로만 진행할시 시간초과 발생하는 코드입니다.

순서가 강제되도록 만들어서 최적 부분구조가 성립한다 생각했지만, 코드의 방향 그래프에 따라 순서가 달라질 수 있으므로 적용이 안됩니다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
static int N, M, S, P, K, A, B, X, L, R;
static int answer = 0;
static ArrayList<Integer>[] graph = new ArrayList[10001];
static PriorityQueue<Integer> pq = new PriorityQueue<>();
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());
for(int i=0;i<N; i++) {
graph[i] = new ArrayList<Integer>();
}
for(int i=0; i<M; i++) {
st = new StringTokenizer(br.readLine());
int A = Integer.parseInt(st.nextToken()) - 1;
int B = Integer.parseInt(st.nextToken()) - 1;
graph[B].add(A);
}
for(int i=0;i<N; i++) {
int temp = DFS(i, new boolean[N]);
if(answer < temp) {
answer = temp;
pq.clear();
pq.offer(i);
}else if(answer == temp) {
pq.offer(i);
}
}
for(int v : pq) {
System.out.print( (v+1) +" ");
}
}
static int DFS(int now, boolean[] visited) {
visited[now] = true;
int ret = 0;
for(int connected = 0; connected < graph[now].size(); connected++) {
int nextNode = graph[now].get(connected);
if(visited[nextNode] == true) continue;
ret += DFS(nextNode, visited) + 1;
}
return ret;
}
}

 

+ Recent posts