問題はこちら
問題概要
個の料理があり,それぞれ作るためにオーブンを使う時間はである.つのオーブンを用いてすべての料理を作るのにかかる時間は最短何分か.解説
つのオーブンのみを用いることを考えると,が答え.つのオーブンだと,料理をつのグループに分けた時のがかかる時間となる.以降どのように料理をつのグループに分けるかが問題となる.
通り試していたら時間がかかるので別の方法を考える.
をの総和()とおく.
を決めるとが決まるのでとなるから,を最小にするはどのようなものかを考える.
ここで,とする(のときは,とを入れ替えることでとなる).このとき,かかる時間はであり,これを小さくするにはを大きくする必要がある.を変形するととなるので,個の料理をいくつか選んでできるの総和のうち以下の最大値が答えになる.これはナップサック問題とか部分和問題のようにDPで解ける.
ナップサック問題とか部分和問題を知らない人はEDPCとかで練習してください.
おまけ
慣れてないとが大きい方からが少ないオーブンに振り分けていくような貪欲が思いつくかもしれませんが,様々な例を考えてみるとWAとなるようなものが見つかってしまいます(4,3,3,2,2など).このような直感に頼った貪欲は危険なのでやめましょう.面倒かもしれませんが貪欲の正当性の証明をしてください.おまけ2
途中で最大値の最小化みたいな話が出てきますが,よく「最大値の最小化は二分探索!」と言われているので二分探索を考えてしまうかもしれません.ここで,なぜ最大値の最小化を二分探索により解くことができる問題があるかを説明します.最大値の最小化はのような形になり,のような部分は扱うのが難しいことがあります.ここで,であることと,任意のに対してであることは同値なので,を決めうつことでというがたがいに関係し合う式から,という各について独立に考えることができる式に変換できます.さらに,「に対してであるか」の真偽は単調であるので二分探索が可能になります.
今回の問題では,です.を決めたとき,やは元の問題より簡単に求まるわけではなさそうなので二分探索で解くのは微妙です(解けないことはないが意味ない).
二分探索で解けるかな?と思うことは悪いことではないですが,このように考えることですぐに無理そうだな(意味ないな)と思うことができます.
例題:https://yukicoder.me/problems/no/710
提出プログラム
pythonのプログラムです.import numpy as np N = int(input()) T = list(map(int, input().split())) S = np.sum(T) DP = np.zeros(S//2+1,dtype=np.bool) DP[-1] = 1 for i in range(N): if S//2+1-T[i] < 0: continue DP[:S//2+1-T[i]] |= DP[T[i]:] #numpyを用いていっぺんに遷移 print(S-S//2+np.argmax(DP))