백준 2668: 숫자고르기 [Java] -포포

2022. 12. 15. 03:16알고리즘/BFS 알고리즘

문제

세로 두 줄, 가로로 N개의 칸으로 이루어진 표가 있다. 첫째 줄의 각 칸에는 정수 1, 2, …, N이 차례대로 들어 있고 둘째 줄의 각 칸에는 1이상 N이하인 정수가 들어 있다. 첫째 줄에서 숫자를 적절히 뽑으면, 그 뽑힌 정수들이 이루는 집합과, 뽑힌 정수들의 바로 밑의 둘째 줄에 들어있는 정수들이 이루는 집합이 일치한다. 이러한 조건을 만족시키도록 정수들을 뽑되, 최대로 많이 뽑는 방법을 찾는 프로그램을 작성하시오. 예를 들어, N=7인 경우 아래와 같이 표가 주어졌다고 하자.

이 경우에는 첫째 줄에서 1, 3, 5를 뽑는 것이 답이다. 첫째 줄의 1, 3, 5밑에는 각각 3, 1, 5가 있으며 두 집합은 일치한다. 이때 집합의 크기는 3이다. 만약 첫째 줄에서 1과 3을 뽑으면, 이들 바로 밑에는 정수 3과 1이 있으므로 두 집합이 일치한다. 그러나, 이 경우에 뽑힌 정수의 개수는 최대가 아니므로 답이 될 수 없다.

 

입력

첫째 줄에는 N(1≤N≤100)을 나타내는 정수 하나가 주어진다. 그 다음 줄부터는 표의 둘째 줄에 들어가는 정수들이 순서대로 한 줄에 하나씩 입력된다.

출력

첫째 줄에 뽑힌 정수들의 개수를 출력하고, 그 다음 줄부터는 뽑힌 정수들을 작은 수부터 큰 수의 순서로 한 줄에 하나씩 출력한다

예제 입력 1 

7
3
1
1
5
5
4
6

예제 출력 1 

3
1
3
5

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

 

2668번: 숫자고르기

세로 두 줄, 가로로 N개의 칸으로 이루어진 표가 있다. 첫째 줄의 각 칸에는 정수 1, 2, …, N이 차례대로 들어 있고 둘째 줄의 각 칸에는 1이상 N이하인 정수가 들어 있다. 첫째 줄에서 숫자를 적절

www.acmicpc.net


문제를 보고 싸이클이 생기면 싸이클의 요소를 List에 담아야겠다고 생각했다.
문제에서 주어진 예시는 다음 그림과 같은 싸이클이 존재한다.

그렇다면 싸이클 체크를 어떻게 할 지만 고민하면 된다.

싸이클은 결국 출발한 지점으로 다시 돌아온다.
따라서 다음 행선지가 이미 boolean visit[] 배열에 방문처리가 완료되었다면 싸이클이라고 할 수 있겠다.

1부터 N까지 차례대로 싸이클이 존재하는지 dfs를 이용해 검사한다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class Main {
    static int n;
    static int from;
    static int[] nums;
    static List<Integer> answer = new LinkedList<>();
    static boolean[] visit;
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());

        nums = new int[n + 1];
        visit = new boolean[n + 1];

        for (int i = 1; i <= n; i++) {
            nums[i] = Integer.parseInt(br.readLine());
        }
		
        //1부터 차례대로 n까지 싸이클을 검사한다.
        for (int i = 1; i <= n; i++) {
            visit[i] = true;
            from = i;
            dfs(i);
            visit[i] = false;
        }

        Collections.sort(answer);

        System.out.println(answer.size());
        for (Integer integer : answer) {
            System.out.println(integer);
        }
    }

    private static void dfs(int start) {
        //다음 행선지
        int next = nums[start];

        if(!visit[next]){
            visit[next] = true;
            dfs(next);
            visit[next] = false;
        }
        
        //사이클이 생기면 경로를 answer에 저장한다.
        if (next == from) {
            //다음 행선지가 시작점인 from이라면 싸이클이 생긴 것이다.
            answer.add(from);
        }
    }
}


코드를 바탕으로 풀이를 설명하면
1부터 N까지 차례대로 싸이클이 생기는지 검사한다고 하였다.
만약 1-> 5 -> 3- >1의 싸이클이 존재한다면 
1을 검사할 때 dfs의 종착점은 1이 되므로 answer에 추가될 것이다.
3을 검사할 때 3 -> 1 -> 5 -> 3으로 종착점이 다시 3이므로 answer에 추가될 것이다..

무튼 이런 방식으로 1부터 N까지 싸이클에 속하는 숫자들을 전부 다 체크할 수 있게된다.