{ }codelper
최대공약수모두 해 보기재귀P0185

기어 열차 짜기

모터는 1분에 R_in번 돌고, 기계는 1분에 정확히 R_out번 돌아야 한다. 그 사이를 기어 열차로 잇는다. 열차의 한 단은 톱니 수가 서로 다른 기어 두 개, 구동 기어 a(톱니 a개)와 따라 도는 기어 b(톱니 b개)로 이루어지고, 그 단을 지나면 회전수가 a/b배가 된다. 쓸 수 있는 기어의 톱니 수가 m가지 주어지고, 같은 크기의 기어는 몇 개든 있다(같은 단을 여러 번 써도 된다). 열차는 0단부터 3단까지 쓸 수 있고, 출력 회전수는 R_in × (각 단의 a/b를 모두 곱한 값)이다. 단의 순서는 결과에 상관이 없으므로, 같은 단들을 순서만 바꾼 열차는 같은 열차로 센다. 출력 회전수가 정확히 R_out이 되는 열차 가운데 가장 적은 단 수 k와, 단이 k개인 그런 열차의 수를 빈칸 하나로 띄워 출력하라. 0단 열차(아무 단도 없음)는 R_in = R_out일 때만 맞고, 그때 답은 "0 1"이다. 3단 이하로 만들 수 없으면 -1을 출력한다.

입력
첫 줄에 R_in과 R_out, 둘째 줄에 m, 셋째 줄에 서로 다른 톱니 수 m개.
1 ≤ R_in, R_out ≤ 10,000, 1 ≤ m ≤ 8, 6 ≤ 톱니 수 ≤ 200
출력
"k 개수" 또는 -1
예시 1 · stdin
640 250
3
16 20 32
stdout
2 1
예시 2 · stdin
800 400
4
6 12 24 48
stdout
1 3
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

단 하나는 많아야 8 × 7 = 56가지라 3단까지 모두 골라 봐도 3만 가지 남짓이에요.
그래서 재귀로 단을 하나씩 고르며 모두 해 봐요.
다음 단은 지금 고른 단의 번호부터만 고르면(번호가 줄지 않게) 순서만 바뀐 열차를 두 번 세지 않아요.
비교는 분수로 해요: 바라는 비 R_out/R_in을 gcd로 기약분수로 줄여 두고, 단을 고를 때마다 분자·분모를 곱한 뒤 다시 gcd로 나눠요.
기약분수는 하나뿐이라 분자끼리, 분모끼리 같은지만 보면 돼요.
함정 ① 바라는 비를 줄이지 않으면 900/1200처럼 줄일 수 있는 입력에서 하나도 못 찾아요.
② 회전수를 실수로 곱해 가며 ==로 비교하면 (x * a) / b의 반올림 오차 때문에 딱 맞는 열차를 놓쳐요.
JavaScript·TypeScript·크레이콘은 수가 모두 실수라 특히 조심해야 하는데, 정수만 곱하고 나누어떨어질 때만 나누면 2⁵³까지 정확해요.
③ 다음 단을 처음부터 다시 고르면 같은 열차를 순서마다 따로 세요.
gcd는 Python math.gcd, C++17 std::gcd(<numeric>)가 있고, Java·C#·C·JavaScript·TypeScript·크레이콘은 유클리드 호제법을 직접 써요.
크레이콘은 수학.몫 대신 /를 써도 되는데, gcd로 나누면 늘 나누어떨어지기 때문이에요.

Pythonmain.py · 33 줄
from math import gcd  r_in, r_out = map(int, input().split())m = int(input())teeth = list(map(int, input().split()))stages = [(a, b) for a in teeth for b in teeth if a != b]  # (구동 기어, 따라 도는 기어) g = gcd(r_out, r_in)want = (r_out // g, r_in // g)  # 바라는 비 = 출력 / 입력, 기약분수로  def count_trains(start, left, num, den):    """stages[start:]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 want가 되는 열차 수.    고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다."""    if left == 0:        return 1 if (num, den) == want else 0    total = 0    for i in range(start, len(stages)):        a, b = stages[i]        n2, d2 = num * a, den * b        c = gcd(n2, d2)        total += count_trains(i, left - 1, n2 // c, d2 // c)  # 늘 기약분수로 줄여 둔다    return total  answer = "-1"for k in range(4):    c = count_trains(0, k, 1, 1)    if c > 0:        answer = f"{k} {c}"        breakprint(answer)
JavaScriptmain.js · 38 줄
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [rIn, rOut, m] = data;const teeth = data.slice(3, 3 + m); function gcd(a, b) {  while (b !== 0) [a, b] = [b, a % b];  return a;} const stages = []; // [구동 기어, 따라 도는 기어]for (const a of teeth) for (const b of teeth) if (a !== b) stages.push([a, b]); const g = gcd(rOut, rIn);const wantNum = rOut / g; // 바라는 비 = 출력 / 입력, 기약분수로const wantDen = rIn / g; // stages[start..]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.// 고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다function countTrains(start, left, num, den) {  if (left === 0) return num === wantNum && den === wantDen ? 1 : 0;  let total = 0;  for (let i = start; i < stages.length; i++) {    const [a, b] = stages[i];    const c = gcd(num * a, den * b);    total += countTrains(i, left - 1, (num * a) / c, (den * b) / c); // 늘 기약분수로 줄여 둔다  }  return total;} let answer = "-1";for (let k = 0; k <= 3; k++) {  const c = countTrains(0, k, 1, 1);  if (c > 0) {    answer = `${k} ${c}`;    break;  }}console.log(answer);
TypeScriptmain.ts · 39 줄
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const [rIn, rOut, m] = data;const teeth: number[] = data.slice(3, 3 + m); function gcd(a: number, b: number): number {  while (b !== 0) [a, b] = [b, a % b];  return a;} const stages: [number, number][] = []; // [구동 기어, 따라 도는 기어]for (const a of teeth) for (const b of teeth) if (a !== b) stages.push([a, b]); const g = gcd(rOut, rIn);const wantNum = rOut / g; // 바라는 비 = 출력 / 입력, 기약분수로const wantDen = rIn / g; // stages[start..]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.// 고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다function countTrains(start: number, left: number, num: number, den: number): number {  if (left === 0) return num === wantNum && den === wantDen ? 1 : 0;  let total = 0;  for (let i = start; i < stages.length; i++) {    const [a, b] = stages[i];    const c = gcd(num * a, den * b);    total += countTrains(i, left - 1, (num * a) / c, (den * b) / c); // 늘 기약분수로 줄여 둔다  }  return total;} let answer = "-1";for (let k = 0; k <= 3; k++) {  const c = countTrains(0, k, 1, 1);  if (c > 0) {    answer = `${k} ${c}`;    break;  }}console.log(answer);
JavaMain.java · 51 줄
import java.util.*; public class Main {    static List<int[]> stages = new ArrayList<>(); // {구동 기어, 따라 도는 기어}    static long wantNum, wantDen;     static long gcd(long a, long b) {        while (b != 0) {            long t = a % b;            a = b;            b = t;        }        return a;    }     // stages[start..]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.    // 고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다    static long countTrains(int start, int left, long num, long den) {        if (left == 0) return num == wantNum && den == wantDen ? 1 : 0;        long total = 0;        for (int i = start; i < stages.size(); i++) {            long n2 = num * stages.get(i)[0], d2 = den * stages.get(i)[1];            long c = gcd(n2, d2);            total += countTrains(i, left - 1, n2 / c, d2 / c); // 늘 기약분수로 줄여 둔다        }        return total;    }     public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        long rIn = sc.nextLong(), rOut = sc.nextLong();        int m = sc.nextInt();        int[] teeth = new int[m];        for (int i = 0; i < m; i++) teeth[i] = sc.nextInt();        for (int a : teeth)            for (int b : teeth)                if (a != b) stages.add(new int[] {a, b});        long g = gcd(rOut, rIn);        wantNum = rOut / g; // 바라는 비 = 출력 / 입력, 기약분수로        wantDen = rIn / g;        String answer = "-1";        for (int k = 0; k <= 3; k++) {            long c = countTrains(0, k, 1, 1);            if (c > 0) {                answer = k + " " + c;                break;            }        }        System.out.println(answer);    }}
C#Main.cs · 54 줄
using System;using System.Collections.Generic;using System.Linq; class Program{    static readonly List<(long a, long b)> stages = new List<(long a, long b)>(); // (구동 기어, 따라 도는 기어)    static long wantNum, wantDen;     static long Gcd(long a, long b)    {        while (b != 0) (a, b) = (b, a % b);        return a;    }     // stages[start..]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.    // 고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다    static long CountTrains(int start, int left, long num, long den)    {        if (left == 0) return num == wantNum && den == wantDen ? 1 : 0;        long total = 0;        for (int i = start; i < stages.Count; i++)        {            long n2 = num * stages[i].a, d2 = den * stages[i].b;            long c = Gcd(n2, d2);            total += CountTrains(i, left - 1, n2 / c, d2 / c); // 늘 기약분수로 줄여 둔다        }        return total;    }     static void Main()    {        long[] r = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(long.Parse).ToArray();        Console.ReadLine();        int[] teeth = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();        foreach (int a in teeth)            foreach (int b in teeth)                if (a != b) stages.Add((a, b));        long g = Gcd(r[1], r[0]);        wantNum = r[1] / g; // 바라는 비 = 출력 / 입력, 기약분수로        wantDen = r[0] / g;        string answer = "-1";        for (int k = 0; k <= 3; k++)        {            long c = CountTrains(0, k, 1, 1);            if (c > 0)            {                answer = k + " " + c;                break;            }        }        Console.WriteLine(answer);    }}
C++main.cpp · 42 줄
#include <iostream>#include <numeric>#include <utility>#include <vector> std::vector<std::pair<long long, long long>> stages;  // (구동 기어, 따라 도는 기어)long long wantNum, wantDen; // stages[start..]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.// 고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다long long countTrains(size_t start, int left, long long num, long long den) {    if (left == 0) return num == wantNum && den == wantDen ? 1 : 0;    long long total = 0;    for (size_t i = start; i < stages.size(); i++) {        long long n2 = num * stages[i].first, d2 = den * stages[i].second;        long long c = std::gcd(n2, d2);        total += countTrains(i, left - 1, n2 / c, d2 / c);  // 늘 기약분수로 줄여 둔다    }    return total;} int main() {    long long rIn, rOut;    int m;    std::cin >> rIn >> rOut >> m;    std::vector<long long> teeth(m);    for (auto& t : teeth) std::cin >> t;    for (long long a : teeth)        for (long long b : teeth)            if (a != b) stages.push_back({a, b});    long long g = std::gcd(rOut, rIn);    wantNum = rOut / g;  // 바라는 비 = 출력 / 입력, 기약분수로    wantDen = rIn / g;    for (int k = 0; k <= 3; k++) {        long long c = countTrains(0, k, 1, 1);        if (c > 0) {            std::cout << k << ' ' << c << '\n';            return 0;        }    }    std::cout << "-1\n";}
Cmain.c · 51 줄
#include <stdio.h> static int sa[56], sb[56], nstages = 0; /* 단: 구동 기어 sa, 따라 도는 기어 sb */static long long want_num, want_den; long long gcd(long long a, long long b) {    while (b != 0) {        long long t = a % b;        a = b;        b = t;    }    return a;} /* 단 start번부터 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.   고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다 */long long count_trains(int start, int left, long long num, long long den) {    if (left == 0) return num == want_num && den == want_den;    long long total = 0;    for (int i = start; i < nstages; i++) {        long long n2 = num * sa[i], d2 = den * sb[i];        long long c = gcd(n2, d2);        total += count_trains(i, left - 1, n2 / c, d2 / c); /* 늘 기약분수로 줄여 둔다 */    }    return total;} int main(void) {    long long r_in, r_out;    int m, teeth[8];    scanf("%lld %lld %d", &r_in, &r_out, &m);    for (int i = 0; i < m; i++) scanf("%d", &teeth[i]);    for (int i = 0; i < m; i++)        for (int j = 0; j < m; j++)            if (i != j) {                sa[nstages] = teeth[i];                sb[nstages++] = teeth[j];            }    long long g = gcd(r_out, r_in);    want_num = r_out / g; /* 바라는 비 = 출력 / 입력, 기약분수로 */    want_den = r_in / g;    for (int k = 0; k <= 3; k++) {        long long c = count_trains(0, k, 1, 1);        if (c > 0) {            printf("%d %lld\n", k, c);            return 0;        }    }    puts("-1");    return 0;}
Craykonmain.cray · 64 줄
최대공약수(a, b){    동안 (b != 0)    {        t = a % b;        a = b;        b = t;    }    돌려주기 a;} // stages[start..]에서 left개를 더 골라(같은 단 여러 번 가능) 비가 바라는 비가 되는 열차 수.// 고른 단의 번호가 줄지 않게 해서, 순서만 바뀐 열차를 두 번 세지 않는다열차세기(start, left, num, den){    만약 (left == 0)    {        만약 (num == wantNum && den == wantDen)        {            돌려주기 1;        }        돌려주기 0;    }    total = 0;    반복 (s = start; s < stages.개수; s++)    {        n2 = num * stages[s][0];        d2 = den * stages[s][1];        c = 최대공약수(n2, d2);        total += 열차세기(s, left - 1, n2 / c, d2 / c); // 늘 기약분수로 줄여 둔다(나누어떨어져서 / 로 된다)    }    돌려주기 total;} r = 콘솔.수들읽기();rIn = r[0];rOut = r[1];m = 콘솔.줄읽기().수로변환();teeth = 콘솔.수들읽기();stages = []; // [구동 기어, 따라 도는 기어]각각 (a 에서 teeth){    각각 (b 에서 teeth)    {        만약 (a != b)        {            stages.넣기([a, b]);        }    }}g = 최대공약수(rOut, rIn);wantNum = rOut / g; // 바라는 비 = 출력 / 입력, 기약분수로wantDen = rIn / g;answer = "-1";반복 (k = 0; k <= 3; k++){    cnt = 열차세기(0, k, 1, 1);    만약 (cnt > 0)    {        answer = k + " " + cnt;        그만;    }}콘솔.쓰기(answer);