カロリーから考える丸亀2000円チャレンジ
この記事の主張:丸亀2000円チャレンジにおいて、2021/10/11時点でのレギュ+店舗限メニュー禁止の条件下では、「おろし醤油うどん並」と「いか天」「いなり」「梅おにぎり」「明太子おにぎり」「鮭おにぎり」「こんぶおにぎり」をそれぞれ2つずつ注文するのが、一番カロリーが低い組み合わせである。
ねりうめです。何故か最近身内で「丸亀2000円チャレンジ」が流行っています。丸亀製麺で2000円分食べるチャレンジです。自分もやりました。
様々な攻略法が考案されていますが、カロリーの観点から考察されたものが見当たらなかったので、自分の勉強も兼ねて調べてみました。
各メニューのカロリーは、こことここを参照しました。計算に使用したメニューは、公式HPに掲載されているもののうち、「店舗限定」が書かれていないもののみです。
結論は冒頭に示したとおりです。ぜひお試しください。
以下に、導出過程を示します。
①考えるうどんの絞り込み
本レギュレーションは使用可能うどんが1杯のみなので、まず、どのうどんを食べるか決めることにした。ただし、すべてのうどんを考慮する必要はない。今回はカロリーを最小化するのが目的であるので、「同価格のうち、最もカロリーが低いうどん」についてのみ考慮すればよい。また、「他のうどんより価格が安く、カロリーが高い」うどんについても省いて良い。以下の表が、この条件に当てはまるうどんである。
次に、うどん以外のメニューについて調査した。野菜かき揚げバグってるでしょ。
では、これらをどのように組み合わせれば、最もカロリーの低い組み合わせを見つけられるだろうか。とりあえず、総当りで試してみることにした。
(ここから先はプログラミングの話)
udon = [
[390, 297, "おろし醤油うどん並"],
[460, 400, "とろ玉うどん温並"],
[470, 434, "きつねうどん並"],
[500, 438, "おろし醤油うどん大"],
[570, 544, "とろ玉うどん温大"],
[580, 579, "きつねうどん大"],
[610, 604, "おろし醤油うどん得"],
[680, 786, "とろ玉うどん得"],
[690, 855, "きつねうどん得"],
[730, 1068, "カレーうどん得"]
]
sidemenu = [
[150, 182, "かしわ天"],
[130, 106, "いか天"],
[120, 159, "さつまいも天"],
[110, 151, "かぼちゃ天"],
[120, 156, "ちくわ天"],
[420, 460, "親子丼"],
[120, 121, "いなり"],
[140, 144, "梅"],
[140, 147, "鮭"],
[140, 147, "明太子"],
[140, 153, "こんぶ"],
]
price_goal = 2000
# ①総当りで解いてみる
def rec(price_now, cal_now, buy_list):
if (price_now < 2000):
temp_answer = [0,1e+10,""]
for i in range(len(sidemenu)):
if (buy_list[i] < 2):
new_buy_list = list(buy_list)
new_buy_list[i] += 1
t = rec(price_now + sidemenu[i][0], cal_now + sidemenu[i][1], new_buy_list)
if (t[1] < temp_answer[1]):
temp_answer = list(t)
return temp_answer
else:
ans_list = [price_now, cal_now, buy_list]
return ans_list
def brute_force(id):
price_sum = udon[id][0]
cal_sum = udon[id][1]
answer = rec(price_sum, cal_sum, [0] * len(sidemenu))
return answer
print(brute_force(9))
このプログラムでは、各トッピングに対し、(0,1,2)個購入するパターンを全て試し、その中で一番カロリーが少なく済むものを出力している。
とりあえず全てのパターンについて調べるので、とても長い時間がかかってしまう。一番実行時間が短く済む「カレーうどん得」についてでも、約2時間ほどかかってしまった。
流石にやってられないので、別の手法を試した。
# ②貪欲的に解く
sidemenu_cp = sorted([[x[1]/x[0], x[0], x[1], x[2]] for x in sidemenu])
def greedy(udon_id):
price_now = udon[udon_id][0]
cal_now = udon[udon_id][1]
ans = []
p = 0
count = 0
while(price_now < 2000):
price_now += sidemenu_cp[p][1]
cal_now += sidemenu_cp[p][2] # カロリー
ans.append(sidemenu_cp[p][3])
count += 1
if(count == 2):
p += 1
count = 0
return [udon[udon_id][2], price_now, cal_now, ans]
answer = ["", 0, 1e+10, []]
for u in range(len(udon)):
a = greedy(u)
print(a)
if(a[2] < answer[2]):
answer = a.copy()
print(answer)
あらかじめサイドメニューのコスパを計算しておき、コスパのよいものから順にぶちこむことにした。実行結果は以下の通りである。
うどん名, 値段, カロリー, メニューの順に表示されている。
しかし、これは正しい解ではない。
例えば、「おろし醤油うどん大」については、おにぎり3つをかしわ天2つに替えたほうがカロリーは低くなる。
そこで、厳密な解を求めるために、動的計画法を用いる。詳細は省くけど、こういうとき強いアルゴリズムである。
# ③動的計画法を用いる
udon = [
[390, 297, "おろし醤油うどん並"],
[460, 400, "とろ玉うどん温並"],
[470, 434, "きつねうどん並"],
[500, 438, "おろし醤油うどん大"],
[570, 544, "とろ玉うどん温大"],
[580, 579, "きつねうどん大"],
[610, 604, "おろし醤油うどん得"],
[680, 786, "とろ玉うどん得"],
[690, 855, "きつねうどん得"],
[730, 1068, "カレーうどん得"]
]
sidemenu = [
[150, 182, "かしわ天"],
[130, 106, "いか天"],
[120, 159, "さつまいも天"],
[110, 151, "かぼちゃ天"],
[120, 156, "ちくわ天"],
[420, 460, "親子丼"],
[120, 121, "いなり"],
[140, 144, "梅"],
[140, 147, "鮭"],
[140, 147, "明太子"],
[140, 153, "こんぶ"],
# 2つまでの制約をかけるのが面倒だからこっちを2倍する
[150, 182, "かしわ天"],
[130, 106, "いか天"],
[120, 159, "さつまいも天"],
[110, 151, "かぼちゃ天"],
[120, 156, "ちくわ天"],
[420, 460, "親子丼"],
[120, 121, "いなり"],
[140, 144, "梅"],
[140, 147, "鮭"],
[140, 147, "明太子"],
[140, 153, "こんぶ"]
]
price_goal = 2000
cal_limit = 3000
dp = [[0] * (cal_limit + 1) for _ in range(len(sidemenu) + 1)]
for i in range(len(sidemenu)):
for j in range(3000):
if(j >= sidemenu[i][1]):
dp[i+1][j] = max(dp[i][j], dp[i]
[j-sidemenu[i][1]] + sidemenu[i][0])
else:
dp[i+1][j] = dp[i][j]
for u in udon:
for i in range(3000):
if (u[0] + dp[len(sidemenu)][i] >= 2000):
print(u[2] + ": "+str(u[1] + i))
break
実行結果は以下の通り。
使用うどんとカロリーが表示されている。単純な貪欲と比べると、差があるものがある。
結局さっきの貪欲で解いたパターンが最強だったらしい。たまたまだけど。
というわけでみなさんも丸亀2000円チャレンジしよう!自分もやったので!
colabでのコード置いときます
この記事が気に入ったらサポートをしてみませんか?