区間分割をしていく DP として、一番最初に解くべき問題を意図して作った!! けんちょん本の 3 例題 (ナップサック問題、編集距離、これ) の 1 つでもある。
問題概要
個の要素が一列に並んでいる。これら 個の要素に対して、区間 のコストは で与えられる。
これら 個の要素を何個かの区間に分割していく。区間分割のコストは、下図のように、各区間のコストの総和で与えられる。
区間分割の仕方を最適化したときの、最小コストを求めよ。
制約
解法
次の DP を立てる。
dp[i]
← 最初の 個の要素 のみを考えたときの、区間分割の仕方を最適化したときの最小コスト (要素 間に仕切りを入れるものとする)
このとき、dp[i]
の値を求めるために、最後の仕切り (要素 の間の仕切りを除いて、その手前の仕切り) の位置がどこなのかで場合分して考えることにする。それが要素 間の仕切りである場合、下図のように
- 最初の 個の要素 を最適分割した上で
- 最後の区間 のスコアを足す
という場合が最適となる。よって、DP の更新式は
dp[i] = min(dp[i], dp[j] + c[j][i])
と表せる。
この更新式を実装することで AC できる。計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; const int INF = 1<<29; int main() { int N; cin >> N; vector<vector<int>> c(N+1, vector<int>(N+1)); for (int i = 0; i < N+1; ++i) for (int j = 0; j < N+1; ++j) cin >> c[i][j]; // DP vector<int> dp(N+1, INF); dp[0] = 0; // 初期条件 for (int i = 1; i <= N; ++i) { for (int j = 0; j < i; ++j) { dp[i] = min(dp[i], dp[j] + c[j][i]); } } cout << dp[N] << endl; }