Swift教室 Swift と競技プログラミングの教室

多重ループ

読了目安 約3分

2 重ループで全ペアを列挙する。break が効く範囲とラベル付き break。

この章の目次

ループの中にもう 1 つループを入れた形を、2 重ループと呼びます。 考えられる候補をすべて調べる解き方を全探索と呼び、2 重ループは「すべての組み合わせ」の全探索に使う基本の道具です。

2 重ループの動き

外側が 1 回進むごとに、内側のループが最初から最後まで回ります。

Swift
for i in 0..<3 {
    for j in 0..<3 {
        print("i = \(i), j = \(j)")
    }
}

全部で 3 × 3 = 9 行が出力されます。

試してみよう: 内側を 0..<5 に変えて、出力が 15 行になることを確かめてください。

全ペアの列挙

配列から 2 つ選ぶ組み合わせは、内側の ji + 1 から始めると重複なく列挙できます。

Swift
let a = [2, 5, 4, 3]
for i in 0..<a.count {
    for j in i + 1..<a.count {
        print("\(a[i])\(a[j])")
    }
}

内側も 0..<a.count にすると、自分自身とのペアや順番違いの重複が混ざります。 i + 1 始まりが「2 つ選ぶ」の定石です。

break と continue は内側だけに効く

breakcontinue が効くのは、それを囲む一番内側のループだけです。

Swift
for i in 0..<3 {
    for j in 0..<3 {
        if j == 1 {
            break  // 内側の for だけを抜ける
        }
        print("i = \(i), j = \(j)")
    }
}

内側で break しても、外側の i のループは続いています。

ラベル付き break

外側のループごと抜けたいときは、ループに名前を付けて break 名前 と書きます。 この名前をラベルと呼びます。

Swift
outer: for i in 0..<3 {
    for j in 0..<3 {
        if i + j == 3 {
            break outer  // 外側の for ごと抜ける
        }
        print("i = \(i), j = \(j)")
    }
}

計算回数を意識する

2 重ループの実行回数はおよそ N × N 回です。 N = 1,000 なら 100 万回ですが、N = 100,000 なら 100 億回になり、現実的な時間で終わりません。 この見積もり方は 計算量 で扱います。

学びどころ

概念一言まとめ
2 重ループ外側が 1 回進むごとに内側が全部回る
全ペアの列挙内側は i + 1 始まりが定石
break / continue一番内側のループにだけ効く
ラベル付き breakouter: forbreak outer で外側ごと抜ける
計算回数2 重ループは N × N 回

演習

果物屋さんに N 個の果物が並んでいて、それぞれに値段が付いています。 ちょうど 2 個買って、合計をぴったり S 円にします。 買い方が何通りあるかを出力してください。

同じ値段でも、別の果物なら別の買い方として数えます。 1 行目に N と S、2 行目に N 個の値段が空白区切りで入力されます。 入力例では 2 円と 5 円の組だけが 7 円になるので、答えは 1 です。

制約: 2 ≤ N ≤ 50、値段は 1 以上 1000 以下、1 ≤ S ≤ 2000

テキスト
入力例:
3 7
2 5 4

出力例:
1
模範解答
Swift
let firstLine = readLine()!.split(separator: " ").map { Int($0)! }
let n = firstLine[0]
let s = firstLine[1]
let a = readLine()!.split(separator: " ").map { Int($0)! }
var count = 0
for i in 0..<n {
    for j in i + 1..<n {
        if a[i] + a[j] == s {
            count += 1
        }
    }
}
print(count)