ABC178 D - Redistribution

備忘録

問題

atcoder.jp

回答

import sys, os, math, bisect, itertools, collections, heapq, queue, copy, array

# from scipy.sparse.csgraph import csgraph_from_dense, floyd_warshall
# from decimal import Decimal
# from collections import defaultdict, deque

sys.setrecursionlimit(10000000)

ii = lambda: int(sys.stdin.buffer.readline().rstrip())
il = lambda: list(map(int, sys.stdin.buffer.readline().split()))
fl = lambda: list(map(float, sys.stdin.buffer.readline().split()))
iln = lambda n: [int(sys.stdin.buffer.readline().rstrip()) for _ in range(n)]

iss = lambda: sys.stdin.buffer.readline().decode().rstrip()
sl = lambda: list(map(str, sys.stdin.buffer.readline().decode().split()))
isn = lambda n: [sys.stdin.buffer.readline().decode().rstrip() for _ in range(n)]

lcm = lambda x, y: (x * y) // math.gcd(x, y)

MOD = 10 ** 9 + 7
INF = float('inf')


def main():
    if os.getenv("LOCAL"):
        sys.stdin = open("input.txt", "r")

    S = ii()
    dp = [0] * (S + 1)

    dp[0] = 1
    # 1 <= i <= Sの範囲を探索
    for i in range(1, S + 1):
        # 0 <= j <= i - 3の範囲で、
        # 間仕切りを置くことができる個数の合計を集計
        if i > 2:
            dp[i] = sum(dp[0:i - 2])
            dp[i] %= MOD
    print(dp[-1])


if __name__ == '__main__':
    main()

考え方

動的計画法で3以上の区間
分けることが出来る回数を求める。


与えられるSS個の並んだ石が存在すると考え、
並んだ石に対して、間仕切りを立てて3個以上連続するグループ分けするイメージで考えた。

例えばS = 7のとき、石が7つ並んでいる。
〇 〇 〇 〇 〇 〇 〇

これに対して、先頭の隙間から間仕切りを立てることが出来るか否かを探索する。
例えば、隙間0の位置は、
間仕切りを立てることができる。
│〇 〇 〇 〇 〇 〇 〇となり、連続する石は全て3つ以上となる。

次に隙間1, 2の位置は、
間仕切りを立てることができない。
〇 │ 〇 〇 〇 〇 〇 〇や、〇 〇 │ 〇 〇 〇 〇 〇は連続する石が3つ以下になるため。

次に隙間3の位置について考える。
ここには間仕切りを立てることができる。
〇 〇 〇 │ 〇 〇 〇 〇となり、分けられたグループは2つとなり、
連続する石はどちらも3つ以上になっている。

隙間4についても、隙間3とほぼ同一の形で間仕切りを立てることは可能。

最後に、隙間5, 6について考えると、
この位置では、間仕切りを立てた後に、右のグループが必ず3つ以下なるため、探索は不要となる。

以上のようなイメージで、各隙間iに対して、
その時点までに立てることができる間仕切りの総数を求めて回答をした。