AOJ ALDS1_10_B : Matrix Chain Multiplication
AOJ ALDS1_10_B : Matrix Chain Multiplication についてのメモを残します。
参考
- 「プログラミングコンテスト攻略のためのアルゴリズムとデータ構造」(螺旋本)
区間 DP
螺旋本では、漸化式を立ててループ (非再帰) で計算する方法が説明されています。
#include <bits/stdc++.h> using namespace std; int main() { int N; cin >> N; vector<int> p(N+1); for (int i = 1; i <= N; i++) cin >> p.at(i-1) >> p.at(i); const int INF = 1000000007; // dp[i][j] := M_i M_i+1 ... M_j の掛け算の回数の最小値 vector<vector<int>> dp(N+1, vector<int>(N+1, INF)); for (int i = 1; i <= N; i++) dp.at(i).at(i) = 0; // 行列の個数 2 個, 3 個, ... と増やしていく for (int length = 2; length <= N; length++) { for (int i = 1; i <= N - length + 1; i++) { int j = i + length - 1; for (int k = i; k <= j-1; k++) { int tmp = p.at(i-1) * p.at(k) * p.at(j); // M_i ... M_k と M_k+1 ... M_j の掛け算の回数 int cnt = dp.at(i).at(k) + dp.at(k+1).at(j) + tmp; dp.at(i).at(j) = min(dp.at(i).at(j), cnt); } } } cout << dp.at(1).at(N) << endl; }
メモ化再帰
たとえば、連鎖行列積 の場合、
- と の積
- と の積
- と の積
これらのうち、掛け算の回数が少ないものを採用する。
#include <bits/stdc++.h> using namespace std; const int INF = 1000000007; vector<int> p; vector<vector<int>> dp; // func(i, j) := 積 M_i M_i+1 ... M_j の掛け算回数の最小値を返す int func(int i, int j) { if (i == j) return dp.at(i).at(j) = 0; if (dp.at(i).at(j) != INF) return dp.at(i).at(j); int res = INF; for (int k = 0; k < j - i; k++) { int tmp = p.at(i-1) * p.at(i+k) * p.at(j); // M_i ... M_i+k と M_i+k+1 ... M_j の積 int cnt = func(i, i+k) + func(i+k+1, j) + tmp; res = min(res, cnt); } return dp.at(i).at(j) = res; } int main() { int N; cin >> N; p.resize(N+1); for (int i = 1; i <= N; i++) cin >> p.at(i-1) >> p.at(i); dp = vector<vector<int>>(N+1, vector<int>(N+1, INF)); cout << func(1, N) << endl; }