公式

D - 作業グループの効率化 / Optimizing Work Groups 解説 by kyopro_friends


この問題は DP で答えを求めることができます。

\(\mathrm{dp}[n]\) を、社員 \(1,2,\ldots,n\) をグループに分けるときの生産性の最大値とします。求める答えは \(\mathrm{dp}[N]\) です。

社員 \(n\) と同じグループになるのが何番の人までかを考えることで、

\(\mathrm{dp}[n]=\max_{0\leq k < n}(\mathrm{dp}[k]+(n-k)\times\sum_{i=k+1}^n P_i)\)

となります。
予め \(P\) の累積和を求めておくことで、 \(\sum_{i=l}^{r}P_i\) は任意の \((l,r)\) に対して \(O(1)\) で得られるとしてよく、このDPは状態数 \(O(N)\) 遷移 \(O(N)\) であるため \(O(N^2)\)\(\mathrm{dp}[N]\) を求めることができます。

実装例 (C++)

#include<bits/stdc++.h>
using namespace std;

int main(){
  int n;
  cin >> n;
  vector<int> p(n);
  for(int i=0; i<n; i++) cin >> p[i];
  vector<long long> sump(n+1);
  for(int i=0; i<n; i++){
    sump[i+1] = sump[i] + p[i];
  }

  vector<long long> dp(n+1, -1e18);
  dp[0] = 0;
  for(int i=1; i<=n; i++){
    for(int j=0; j<i; j++){
      dp[i] = max(dp[i], dp[j] + (sump[i] - sump[j]) * (i - j));
    }
  }

  cout << dp.back() << endl;
}

実装例 (Python)

N = int(input())
P = list(map(int, input().split()))
sumP = [0]
for p in P:
  sumP.append(sumP[-1] + p)

dp = [-10**18] * (N+1)
dp[0] = 0
for i in range(1, N+1):
  dp[i] = max(dp[j] + (sumP[i] - sumP[j]) * (i - j) for j in range(i))

print(dp[-1])

投稿日時:
最終更新: