C - 遠足のおやつ / Field Trip Snacks 解説 by admin
GPT 5.2 High概要
各おやつを必ず \(T\) 個ずつ買うとき、予算 \(E\) 円以内で買える「おやつの種類数」の最大値を求めます。結論として、安いおやつから順に選ぶ貪欲法で最大化できます。
考察
重要な気づき
おやつ \(i\) を選ぶと必ず \(P_i \times T\) 円かかります。したがって条件は
\(\displaystyle \sum_{i \in S} (P_i \times T) \le E\)
です。
ここで \(T\) は全ての種類で共通なので、両辺を \(T\) で割る発想が有効です。
\(\displaystyle \sum_{i \in S} P_i \le \left\lfloor \frac{E}{T} \right\rfloor\)
となり、「値段 \(P_i\) の品物をいくつか選び、合計がある上限以内になるようにしたとき、何個(何種類)選べるか」という問題に変形できます。
なぜ安い順が最適か
「種類数」を最大化したいので、同じ予算なら 1 種類あたりのコストが小さいものを優先すべきです。
たとえば、残り予算が限られているときに高いおやつを選んでしまうと、そのせいで複数の安いおやつが買えなくなる可能性があります。
逆に、常に安い順に選べば、同じ予算で選べる種類数が最も多くなります(典型的な「合計制約下で個数最大化」問題で、重みが正のときは昇順に詰めるのが最適)。
素朴なアプローチが危険な理由
- 全ての部分集合を試す方法は \(2^N\) 通りで、\(N \le 10^5\) では不可能です。
- 「どの組合せが良いか」を DP(ナップサック)で解くのも、予算 \(E\) が最大 \(10^{14}\) と巨大で現実的ではありません。
そこで、「\(T\) が共通」な点を使って簡約し、さらに貪欲法に落とし込みます。
アルゴリズム
- \(P\) を昇順にソートする。
- 予算を \(T\) 個分で割った上限 \(budget = \left\lfloor \frac{E}{T} \right\rfloor\) を計算する。
このとき「選んだおやつの \(P_i\) の合計が \(budget\) 以下なら、元の条件も必ず満たす」。 - 小さい \(P_i\) から順に見ていき、現在の合計 \(s\) に足しても \(s + P_i \le budget\) なら採用する。
- これ以上採用できなくなった時点で終了し、採用数を答える。
例:\(T=3, E=20, P=[4,2,5,1]\) のとき
\(budget=\lfloor 20/3 \rfloor=6\)、\(P\) をソートして \([1,2,4,5]\)。
\(1+2=3\) までは入るが、次の \(4\) を足すと \(7>6\) なので終了。答えは \(2\) 種類。
計算量
- 時間計算量: \(O(N \log N)\)(ソートが支配的)
- 空間計算量: \(O(N)\)(配列保持)
実装のポイント
\(E\) は最大 \(10^{14}\) なので、言語によっては 64bit 整数が必須です(Python は自動で大丈夫)。
\(\sum P_i \times T \le E\) を直接計算してもよいですが、先に \(budget = E // T\) にして \(\sum P_i \le budget\) とする方が安全・簡潔です。
ソート後は「これ以上入らない」時点で以降も入らないため、ループを
breakして高速化できます。ソースコード
import sys
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
N, T, E = data[0], data[1], data[2]
P = data[3:3 + N]
P.sort()
budget = E // T # sum(P_i) <= floor(E / T)
s = 0
ans = 0
for p in P:
if s + p <= budget:
s += p
ans += 1
else:
break
print(ans)
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: