🔍
[鉄則A14] Four Boxes (半分全列挙)
半分全列挙の基本問題。
問題を要約する
- 4つの箱にそれぞれ N 枚のカードが入っている
- それぞれの箱から適当に1枚引く
- 4枚のカードの合計が K になる可能性はあるか?
例1で問題を理解する
A | B | C | D |
---|---|---|---|
3 | 4 | 10 | 1 |
9 | 7 | 20 | 2 |
17 | 9 | 30 | 3 |
から 9, 9, 30, 2 を引けば K(50) になる。
解き方
- A vs B を総当たりして足した P を作る
- C vs D を総当たりして足した Q を作る
- P + Q = K になるか調べる
最後は Q = K - P
に変形して Q
の中に K - P
があるかを探す。
例1のシミュレーション
A | B | C | D |
---|---|---|---|
3 | 4 | 10 | 1 |
9 | 7 | 20 | 2 |
17 | 9 | 30 | 3 |
から、
総当たり足し算の結果 | |
---|---|
P | 7 10 12 13 16 18 21 24 26 |
Q | 11 12 13 21 22 23 31 32 33 |
を作る。
続いて K(50) - 7
の 43 が Q に含まれるか探す。線形探索だと遅いので二分探索を行う。しかし 43 はなかったので次に進む。これを繰り返すと 18 のときに 50 - 18
の 32 が Q に見つかる。
全探索 (TLE)
N, K = gets.split.collect(&:to_i) # => [3, 50]
A, B, C, D = 4.times.collect { gets.split.collect(&:to_i) } # => [[3, 9, 17], [4, 7, 9], [10, 20, 30], [1, 2, 3]]
ans = N.times.any? do |i|
N.times.any? do |j|
N.times.any? do |k|
N.times.any? do |l|
A[i] + B[j] + C[k] + D[l] == K
end
end
end
end
ans # => true
puts ans ? "Yes" : "No"
最悪 O(N^4) かかってしまう。
解説の二分探索版 (AC)
444 ms
N, K = gets.split.collect(&:to_i) # => [3, 50]
A, B, C, D = 4.times.collect { gets.split.collect(&:to_i) } # => [[3, 9, 17], [4, 7, 9], [10, 20, 30], [1, 2, 3]]
P = A.product(B).collect(&:sum) # => [7, 10, 12, 13, 16, 18, 21, 24, 26]
Q = C.product(D).collect(&:sum).sort # => [11, 12, 13, 21, 22, 23, 31, 32, 33]
ans = P.any? { |e| Q.bsearch { |q| (K - e) - q } } # => true
puts ans ? "Yes" : "No"
find を bsearch で置き換えるなら find-any モードが適している。
集合 (AC)
473 ms
N, K = gets.split.collect(&:to_i) # => [3, 50]
A, B, C, D = 4.times.collect { gets.split.collect(&:to_i) } # => [[3, 9, 17], [4, 7, 9], [10, 20, 30], [1, 2, 3]]
P = A.product(B).collect(&:sum) # => [7, 10, 12, 13, 16, 18, 21, 24, 26]
Q = C.product(D).collect(&:sum).to_set # => #<Set: {11, 12, 13, 21, 22, 23, 31, 32, 33}>
ans = P.any? { |e| Q.include?(K - e) } # => true
puts ans ? "Yes" : "No"
想定の解き方とは異なるが、集合にしてもほぼ同じ時間で解ける。Q は重複していても問題ない。
Discussion