{ }codelper
暴力枚举嵌套循环break / continueP0345

两臂不等长的天平

有一架两臂长度可能不同的天平。
左臂的长度是 a,右臂的长度是 b。
当左盘上砝码的重量乘以 a 的值等于右盘上砝码的重量乘以 b 的值时,天平保持水平。
有 n 个砝码,编号从 1 到 n。
第一行给出 n、a、b,相邻两个之间用一个空格隔开。
第二行按编号顺序给出 n 个砝码的重量,相邻两个之间用一个空格隔开。
现在要选出两个不同的砝码,一个放在左盘,一个放在右盘,使天平保持水平。
即使重量相同,只要编号不同就是不同的砝码。
按这个顺序输出放在左盘的砝码编号和放在右盘的砝码编号,两者用一个空格隔开。
如果有多种方法,输出左盘砝码编号最小的那种;如果仍有多种,输出其中右盘砝码编号最小的那种。
如果没有办法,输出 -1。

输入
1 ≤ n ≤ 200,1 ≤ a ≤ 100,1 ≤ b ≤ 100,1 ≤ 砝码重量 ≤ 1,000(均为整数,只给出数字,不带逗号)
输出
一行,左盘砝码编号和右盘砝码编号,用一个空格隔开;没有办法时输出 -1
样例 1 · stdin
5 2 3
4 6 9 2 3
stdout
2 1
样例 2 · stdin
3 3 3
5 7 9
stdout
-1
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

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

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

用两层循环把左盘砝码和右盘砝码全部试一遍,在第一组合适的地方停下,那就是答案。
一次跳出两层循环的写法,各语言不一样。
Java 和 JS、TS 给外层循环加上 search: 这样的标签,再用 break search; 跳出。
C 用 goto done; 跳到循环外面。
C++ 的解答像 i < n && !found 这样,在两层循环的条件里都放了一个标志变量。
Python、C#、Craykon 的解答把查找部分写成函数,用 return(Craykon 是 返回)跳出。
C# 没有带标签的 break。
陷阱一:普通的 break 只跳出内层循环。
外层循环继续转,后面出现的组合会把答案覆盖掉。
例 1 输出的不是 2 1 而是 5 4。
陷阱二:左和右是不同的位置,所以 (i, j) 和 (j, i) 是不同的方法。
因此 j 也必须从头到尾转一遍。
如果像选两个东西的题目里常见的那样让 j 从 i + 1 开始,例 1 中就看不到右盘编号更小的 2 1,结果输出 -1。
陷阱三:同一个砝码不能同时放在两边,所以要检查 i 和 j 是否不同。
漏掉的话,在 a 和 b 相等的例 2 中会输出 1 1。
陷阱四:左盘砝码乘以 a,右盘砝码乘以 b。
乘反了,例 1 会输出 1 2。

Pythonmain.py · 11 行
def find(n, a, b, w):    for i in range(n):        for j in range(n):            if i != j and w[i] * a == w[j] * b:                return f"{i + 1} {j + 1}"   # return 하나로 두 겹의 반복을 모두 벗어난다    return "-1"  n, a, b = map(int, input().split())w = list(map(int, input().split()))print(find(n, a, b, w))
JavaScriptmain.js · 12 行
const [n, a, b, ...w] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let answer = "-1";// 바깥 반복에 이름표를 붙이면 break 이름표로 두 겹을 한 번에 벗어난다search: for (let i = 0; i < n; i++) {  for (let j = 0; j < n; j++) {    if (i !== j && w[i] * a === w[j] * b) {      answer = `${i + 1} ${j + 1}`;      break search;    }  }}console.log(answer);
TypeScriptmain.ts · 13 行
import { readFileSync } from "node:fs";const [n, a, b, ...w]: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let answer: string = "-1";// 바깥 반복에 이름표를 붙이면 break 이름표로 두 겹을 한 번에 벗어난다search: for (let i = 0; i < n; i++) {  for (let j = 0; j < n; j++) {    if (i !== j && w[i] * a === w[j] * b) {      answer = `${i + 1} ${j + 1}`;      break search;    }  }}console.log(answer);
JavaMain.java · 22 行
import java.util.Scanner; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        int n = sc.nextInt(), a = sc.nextInt(), b = sc.nextInt();        int[] w = new int[n];        for (int i = 0; i < n; i++) w[i] = sc.nextInt();        String answer = "-1";        // 바깥 반복에 이름표를 붙이면 break 이름표로 두 겹을 한 번에 벗어난다        search:        for (int i = 0; i < n; i++) {            for (int j = 0; j < n; j++) {                if (i != j && w[i] * a == w[j] * b) {                    answer = (i + 1) + " " + (j + 1);                    break search;                }            }        }        System.out.println(answer);    }}
C#Main.cs · 24 行
using System; class Program{    // C#에는 이름표 break가 없어서 찾기를 함수로 빼고 return으로 벗어난다    static string Find(int n, int a, int b, int[] w)    {        for (int i = 0; i < n; i++)        {            for (int j = 0; j < n; j++)            {                if (i != j && w[i] * a == w[j] * b) return $"{i + 1} {j + 1}";            }        }        return "-1";    }     static void Main()    {        int[] first = Array.ConvertAll(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries), int.Parse);        int[] w = Array.ConvertAll(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries), int.Parse);        Console.WriteLine(Find(first[0], first[1], first[2], w));    }}
C++main.cpp · 23 行
#include <iostream>#include <vector> int main() {    int n, a, b;    std::cin >> n >> a >> b;    std::vector<int> w(n);    for (int& x : w) std::cin >> x;    int left = -1, right = -1;    bool found = false;    // 두 반복의 조건에 모두 !found를 넣어, 찾는 순간 두 겹이 다 멈추게 한다    for (int i = 0; i < n && !found; i++) {        for (int j = 0; j < n && !found; j++) {            if (i != j && w[i] * a == w[j] * b) {                left = i + 1;                right = j + 1;                found = true;            }        }    }    if (found) std::cout << left << " " << right << "\n";    else std::cout << -1 << "\n";}
Cmain.c · 18 行
#include <stdio.h> int main(void) {    int n, a, b, w[200];    scanf("%d %d %d", &n, &a, &b);    for (int i = 0; i < n; i++) scanf("%d", &w[i]);    for (int i = 0; i < n; i++) {        for (int j = 0; j < n; j++) {            if (i != j && w[i] * a == w[j] * b) {                printf("%d %d\n", i + 1, j + 1);                goto done;   /* break는 안쪽 반복만 벗어나므로 goto로 두 겹을 벗어난다 */            }        }    }    printf("-1\n");done:    return 0;}
Craykonmain.cray · 19 行
// 돌려주기 하나로 두 겹의 반복을 모두 벗어난다찾기(n, a, b, w){    循环 (i = 0; i < n; i++)    {        循环 (j = 0; j < n; j++)        {            如果 (i != j && w[i] * a == w[j] * b)            {                返回 i + 1 + " " + (j + 1);            }        }    }    返回 "-1";} 첫줄 = 控制台.读数字();w = 控制台.读数字();控制台.输出(찾기(첫줄[0], 첫줄[1], 첫줄[2], w));