Official
D - 作業グループの効率化 / Optimizing Work Groups Editorial by admin
DeepSeek V3概要
社員を連続するグループに分割し、各グループの生産性(人数×能力値の合計)の合計を最大化する問題です。
考察
素朴なアプローチとして、全ての分割パターンを列挙すると、分割位置の選び方が \(O(2^N)\) 通りあり、\(N=5000\) では計算が不可能です。そこで動的計画法(DP)を用います。DP[r] を社員1から社員rまでの最適な生産性の合計と定義し、最後のグループの範囲 [l+1, r] を全探索することで状態遷移を行います。ただし、単純な実装では \(O(N^3)\) となり、\(N=5000\) では約1250億回の計算が必要でTLEとなります。そこで、累積和を用いてグループの合計計算を \(O(1)\) にし、全体を \(O(N^2)\) に高速化します。
アルゴリズム
- 累積和配列 prefix_sum を事前計算します。prefix_sum[i] = \(P_1 + P_2 + \cdots + P_i\) となります。
- DP配列を用意し、DP[0] = 0 で初期化します。
- rを1からNまでループし、各rに対してlを0からr-1までループします:
- グループ [l+1, r] のサイズは group_size = r - l
- グループの能力値合計は group_sum = prefix_sum[r] - prefix_sum[l]
- このグループを最後に追加した場合の生産性合計は candidate = DP[l] + group_size × group_sum
- candidate が現在のDP[r]より大きければ更新します
- DP[N]が求める答えとなります。
計算量
- 時間計算量: \(O(N^2)\)
- 空間計算量: \(O(N)\)
実装のポイント
累積和を事前計算することで、部分和の計算を \(O(1)\) にします。
DP配列のインデックスは0-indexedで、DP[r]はr人目までの最適値を表します。
内側のループでlを0からr-1まで探索するため、二重ループで \(O(N^2)\) となりますが、\(N=5000\) では約1250万回の計算で実行可能です。
ソースコード
import sys
def main():
data = sys.stdin.read().split()
n = int(data[0])
P = list(map(int, data[1:1+n]))
prefix_sum = [0] * (n + 1)
for i in range(1, n + 1):
prefix_sum[i] = prefix_sum[i - 1] + P[i - 1]
dp = [0] * (n + 1)
for r in range(1, n + 1):
dp[r] = dp[r - 1] + P[r - 1]
for l in range(r):
group_size = r - l
group_sum = prefix_sum[r] - prefix_sum[l]
candidate = dp[l] + group_size * group_sum
if candidate > dp[r]:
dp[r] = candidate
print(dp[n])
if __name__ == "__main__":
main()
この解説は deepseekv3 によって生成されました。
posted:
last update: