{ }codelper
最大公约数暴力枚举递归P0185

搭一列齿轮

电机每分钟转 R_in 圈,机器必须每分钟恰好转 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()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

一级最多只有 8 × 7 = 56 种,所以选到 3 级也只有 3 万多种。
因此用递归一级一级地选,全部试一遍。
下一级只从当前所选级的编号起选(编号不减),就不会把只是顺序不同的齿轮列重复计数。
比较要用分数:先用 gcd 把想要的比 R_out/R_in 约成最简分数,每选一级就把分子、分母相乘,再除以它们的 gcd。
最简分数是唯一的,所以只需比较分子与分子、分母与分母是否相等。
陷阱 ① 不约简想要的比,像 900/1200 这种可约的输入就一个也找不到。
② 用浮点数连乘转速并用 == 比较,会因为 (x * a) / b 的舍入误差漏掉恰好合适的齿轮列。
JavaScript、TypeScript 和 Craykon 的数全是浮点数,要特别小心;但只做整数乘法、只在能整除时才除,在 2⁵³ 以内是精确的。
③ 下一级又从头选,会把同一列按每种顺序分别计数。
gcd 在 Python 有 math.gcd,C++17 有 std::gcd(<numeric>);Java、C#、C、JavaScript、TypeScript 和 Craykon 要自己写欧几里得算法。
Craykon 这里可以用 / 代替 数学.商,因为除以 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);