-
백준 1010번 다리 놓기알고리즘 문제풀이/Java 2020. 4. 6. 22:38
문제
재원이는 한 도시의 시장이 되었다. 이 도시에는 도시를 동쪽과 서쪽으로 나누는 큰 강이 흐르고 있다. 하지만 재원이는 다리가 없어서 시민들이 강을 건너는데 큰 불편을 겪고 있음을 알고 다리를 짓기로 결심하였다. 강 주변에서 다리를 짓기에 적합한 곳을 사이트라고 한다. 재원이는 강 주변을 면밀히 조사해 본 결과 강의 서쪽에는 N개의 사이트가 있고 동쪽에는 M개의 사이트가 있다는 것을 알았다. (N ≤ M)
재원이는 서쪽의 사이트와 동쪽의 사이트를 다리로 연결하려고 한다. (이때 한 사이트에는 최대 한 개의 다리만 연결될 수 있다.) 재원이는 다리를 최대한 많이 지으려고 하기 때문에 서쪽의 사이트 개수만큼 (N개) 다리를 지으려고 한다. 다리끼리는 서로 겹쳐질 수 없다고 할 때 다리를 지을 수 있는 경우의 수를 구하는 프로그램을 작성하라.
입력
입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.
출력
각 테스트 케이스에 대해 주어진 조건하에 다리를 지을 수 있는 경우의 수를 출력한다.
예제 입력 1 복사
3 2 2 1 5 13 29
예제 출력 1 복사
1 5 67863915
해설
참고로 아래 코드는 위 문제에 대한 답은 아니다. 입력과 출력의 방식만 바꾸면 답이 될 수 있다.
아래 코드는 n개의 원소 중 m개를 고르는 모든 조합을 찾는 알고리즘인데, 위 다리놓기가 바로 그 문제이다.
import java.io.*; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) throws IOException { //BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(System.out)); //String line = br.readLine(); List<Integer> picked = new ArrayList<>(); pick(7, 4, picked); } public static void pick(int n, int toPick, List<Integer> picked){ // n : 원소들의 총 개수 // toPick : 더 골라야 할 원소들의 개수 // picked : 지금까지 고른 원소들의 번호 if(toPick == 0) { for(int num : picked) System.out.print(num); System.out.println(""); return; } int size = picked.size(); for(int i = picked.isEmpty() ? 0 : picked.get(size - 1) + 1; i < n - toPick + 1; ++i){ picked.add(i); pick(n, toPick - 1, picked); picked.remove(size); } } }
위 문제에 대한 풀이는 나중에 다시 달아놓도록 하겠다.
import java.io.*; import java.math.BigInteger; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int testCase = Integer.parseInt(br.readLine()); while(testCase-- > 0){ String[] line = br.readLine().split(" "); BigInteger k = new BigInteger(line[0]); // 뽑는 경우 BigInteger n = new BigInteger(line[1]); // 전체의 경우 // 공식 : n! / (n - k)!k! BigInteger ret = fac(n).divide(fac(n.subtract(k)).multiply(fac(k))); System.out.println(ret); } } public static BigInteger fac(BigInteger n){ if(n.equals(BigInteger.ZERO)) return BigInteger.ONE; return n.multiply(fac(n.subtract(BigInteger.ONE))); } }
공식을 이용하여 문제를 해결했는데, 이 과정에서 BigInteger이라는 클래스를 이용하였다. 다른 답을 보면 대부분 숫자의 크기때문에 배열을 통해서 문제를 해결하였는데, 다음과 같다.
import java.io.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int testCase = Integer.parseInt(br.readLine()); int[][] dp = new int[31][31]; for(int i = 1; i < 31; i++){ for(int j = 0; j <= i; j++){ if(j == 0 || i == j ){ dp[i][j] = 1; } else { dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; } } } while(testCase-- > 0){ String[] line = br.readLine().split(" "); int k = Integer.parseInt(line[0]); // 뽑는 경우 int n = Integer.parseInt(line[1]); // 전체의 경우 System.out.println(dp[n][k]); } } }
확실히 시간측면에서 큰 이득을 볼 수 있다. 앞으로 조합의 문제에서 잘 이용하도록 하자.
느낀 점
- 순열과 조합에 관한 문제였다. 이 부분에 대해 개념이 많이 취약해졌으니 공부할 필요가 있다.
- 처음 저 문제를 봤을 때, 느낌 상 공식이 존재할 거 같다고는 생각했지만, 이 문제가 조합일 줄은 생각도 못했다. 가만 생각해보면, 임의의 다리 네 개를 아무렇게나 놓아도 다시 재배치하면 겹치지 않게 놓을 수가 있다. 이 말은 조합의 정의인 순서를 고려하지 않는 것과 동일하다. 앞으로 순열과 조합 문제가 나오면 이 문제가 순열인지, 조합인지를 분명히 파악하는 습관이 필요할 듯 하다.
'알고리즘 문제풀이 > Java' 카테고리의 다른 글
문제풀이 노트 (0) 2020.04.07 알고스팟 Boggle (0) 2020.04.06 백준 1152번 단어의 개수 (0) 2020.04.05 백준 1233번 주사위 (0) 2020.04.05 백준 1076번 저항 (0) 2020.04.05