ABC 285 E Work or Rest 解説
解説のDPは少し自分にはわかりにくかったので、自分が解いた方法をまとめておきます。
サイクルを考える
次の曜日を考えなくてはならない可能性があるので、簡単にするため、週の最初は休日であると仮定します。
この仮定があっても、求められる最大値は変化しません。
これは、ある並びの休日、平日(
そこで、週はかならず休日から始まると仮定します。
このとき、週は必ず休日+複数の平日からなる小要素に分解できます。
この星では休日までの近さしか評価軸に入らないようなので、連休をあげる必要性はありません。そのため、かならず以下のような形式を取るはずです。
ところでこの小要素の種類は
それぞれのサイクルについて、生成される生産量を考えるのに必要なオーダは
ここまでをコードに書くとこんな感じです。
cycle_costs = {}
for rest_cycle in range(2, N + 1): # O(N^2)
cycle_sum = 0
for i in range(rest_cycle - 1):
x = i
y = rest_cycle - i - 2
cycle_sum += A[min(x, y)]
cycle_costs[rest_cycle] = cycle_sum
cycle_costs
にサイクルあたりの生産量を入れています。
サイクルの組を考える
次に得られた小要素の合計で週を構成してあげなければなりません。
これは、いわゆるナップサック問題です。
適当なサイクルの組み合わせで、週の合計がNになるようにしてあげたときの最大の合計を求めてあげればよいです。
これも、
costs = [-1] * (N + 1)
costs[0] = 0
for cycle in cycle_costs:
cc = cycle_costs[cycle]
for i in range(len(costs)):
if i + cycle > N:
break
if costs[i] != -1:
costs[i+cycle] = max(costs[i+cycle],costs[i] + cc)
print(max(costs))
最終的な解
#!/usr/bin/env python3
import sys
def solve(N: int, A: "List[int]"):
cycle_costs = {}
for rest_cycle in range(2, N + 1): # O(N^2)
cycle_sum = 0
for i in range(rest_cycle - 1):
x = i
y = rest_cycle - i - 2
cycle_sum += A[min(x, y)]
cycle_costs[rest_cycle] = cycle_sum
costs = [-1] * (N + 1)
costs[0] = 0
for cycle in cycle_costs:
cc = cycle_costs[cycle]
for i in range(len(costs)):
if i + cycle > N:
break
if costs[i] != -1:
costs[i+cycle] = max(costs[i+cycle],costs[i] + cc)
print(max(costs))
return
# Generated by 2.12.0 https://github.com/kyuridenamida/atcoder-tools (tips: You use the default template now. You can remove this line by using your custom template)
def main():
def iterate_tokens():
for line in sys.stdin:
for word in line.split():
yield word
tokens = iterate_tokens()
N = int(next(tokens)) # type: int
A = [int(next(tokens)) for _ in range(N)] # type: "List[int]"
solve(N, A)
if __name__ == '__main__':
main()
余談
DPの最後の要素だけみたらひとつだけWAで、最大値を見たらACで引っかかりました。
- WA https://atcoder.jp/contests/abc285/submissions/38072914
- AC https://atcoder.jp/contests/abc285/submissions/38073655
変化しているのはここだけ
print(max(costs)) # AC
print(costs[-1]) # WA
これはつまり、うまくサイクルで割り切れない日が存在した時に、休日が連続するにもかかわらず休みを入れてあげたほうが、そこに仕事の日を入れるよりも効率が良いということになります。
わかるような、わからないような。どんなテストケースならそんなことがありえるのかわかってません。
Discussion