はじめに
高橋君は、少ないお小遣いでTOTOくじ
を楽しもうと思っています。
高橋君が楽しいとは、1等もしくは2等が当たることです。
高橋君が楽しむために最適な戦略を取った場合、合計金額の最小値を求めなさい。
TOTOくじ
ここでは、組合せの少ないtotoGoal3
で計算を進めます。
例1
一番シンプルな例は、3ダブルです。
2の3乗なので本来8通り(800円)必要ですが、2通りで楽しめます。
def check(i)
f = false
['000'.to_i(2), '111'.to_i(2)].each do |x|
f ||= true if (i ^ x).to_s(2).count('1') <= 1
end
ct = i.to_s(2).rjust(3, '0')
puts f ? "#{ct} OK" : "#{ct} NG"
end
(2**3).times do |i|
check(i)
end
def check(i):
ct = format(i, 'b').rjust(3, '0')
for x in ['000', '111']:
f = False
c = 0
for i in range(len(x)):
if ct[i] == x[i]:
c += 1
if c >= 2:
f = True
break
if f:
print(ct, 'OK')
else:
print(ct, 'NG')
for i in range(2**3):
check(i)
# output
000 OK
001 OK
010 OK
011 OK
100 OK
101 OK
110 OK
111 OK
ここでは000
と111
の2通りで、2等以上(はずれが1つ以下)を確認しています。
一つの買い目で全的中とはずれ1つの4通りに対応可能ですので、8÷4=2通りでいいわけです。
Ruby
の方は、xor
を使用していますが、トリプル対応などを考えますと、よろしくないかもしれないです。
応用
応用は、4ダブルです。
3ダブルはシンプルですので、簡単に求めることができましたが、4ダブルの組合せはどのように求めればいいでしょうか。
一つの買い目で5通りに対応可能ですので、(2の4乗)÷5=3.2となり4通り以上が必要と推定されます。
しかし、ここはatcoder
のコンテスト中ではないので、TLE
を気にせず全検索することにします。
def check(cs)
a = (0...2**N).to_a
cmb = []
a.combination(cs) do |xs|
return cmb.size if xs.include?(0).! || cmb.empty?.!
h = Hash.new(0)
a.each do |i|
f = false
xs.each do |x|
if (i ^ x).to_s(2).count('1') <= 1
f = true
h[i] += 1
break
end
end
break if f.!
end
if h.size == 2**N
pt = xs.map{ _1.to_s(2).rjust(N, '0') }
puts pt.join(', ')
cmb << pt
end
end
end
N = gets.to_i
(2**N / N.next.to_f).ceil.upto(2**N) do |zz|
break if check(zz) > 0
end
# input
3
# output
000, 111
# input
4
# output
0000, 0001, 1110, 1111
# input
5
# output
00000, 00001, 00010, 01111, 10111, 11011, 11100
5ダブルまでは、実用的な時間で答えが返ってきます。
補足
TOTOくじは2000年初頭より発売されており、削減の考え方にはそれなりの歴史があります。
例えば、6ダブルの最小値は12
であることが知られており、予め計算結果をテーブルに保存しておくことにより、計算時間を縮小する手法がとられることもあります。
まとめ
- TOTOくじに詳しくなった