問題描述:ios
一個n×m矩陣由n行m列共n×m個數排列而成。兩個矩陣A和B可 以相乘當且僅當A的列數等於B的行數。一個n×m的矩陣乘以一個m×p的矩陣等於 一個n×p的矩陣,運算量爲m×n×p。矩陣乘法不知足分配律,但知足結合律,所以A×B×C能夠按順序(A×B)×C進行也能夠按A×(B×C)來進行。假設A、B、C分別 是2×3,3×4,4×5的,則(A×B)×C的運算量爲2×3×4+2×4×5=64,A×(B×C)的運算 量爲3×4×5+2×3×5=90。顯然第一種順序節省運算量。給出n個矩陣組成的序列,設計一種方法把它們乘起來,使得總的運算量儘可能小。假設第i個矩陣Ai是p[i−1] ×p[i]的。 spa
分析:設計
能夠把這n個矩陣分紅兩部分,P = A1 x A2 x A3...Ak和Q = Ak+1 x Ak+2...An,1<= k < n,顯然結果就是k循環一遍,取其中PxQ結果最小的。code
dp[i][j]表示從第i個矩陣乘到第j個矩陣所須要的最小代價。blog
狀態轉移方程:dp[i][j] = min(dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j]), i <= k < jci
代碼實現:get
寫法一:記憶化搜索io
int solve(int i, int j) { if(dp[i][j] < minn) return dp[i][j]; if(i == j) dp[i][j] = 0; else { for(int k = i; k < j; k++) { int ans = solve(i, k) + solve(k + 1, j) + p[i - 1] * p[k] * p[j]; if(ans < dp[i][j]) dp[i][j] = ans; } } return dp[i][j]; }
寫法二:遞推class
for(int i = 0; i < n; i++) { //長度爲1時 dp[i][i] = 0; } for(int len = 2; len <= n; len++) { //從小到大依次求出相應長度時的狀況 for(int i = 1; i < n - len + 1; i++) { j = i + len - 1; for(k = i; k < j; k++) { int ans = dp[i][k] + dp[k + 1][j] + p[i - 1] * p[k] * p[j]; if(ans < dp[i][j]) dp[i][j] = ans; } } }
看一道例題stream
題目連接:
http://poj.org/problem?id=1651
題意:
給你n張牌,每次抽一張,每次抽取的價值等於這張牌的值乘以這張牌左右兩張的值,其中,第一張和最後一張不能抽,問你怎樣的抽取方案可以取到最小价值
分析:
典型的最優矩陣鏈乘,直接看代碼理解吧
代碼:
1 #include<iostream> 2 using namespace std; 3 int dp[110][110]; //dp[i][j]表示從i到j中(不包括i和j)抽數能獲得的最小值 4 int num[110]; 5 int main() 6 { 7 int n; 8 cin >> n; 9 for(int i = 0; i < n; i++) 10 cin >> num[i]; 11 12 for(int i = 0; i < n - 2; i++) 13 dp[i][i + 2] = num[i] * num[i + 1] * num[i + 2]; 14 15 for(int len = 3; len < n; len++) 16 { 17 for(int i = 0; i + len < n; i++) 18 { 19 int j = i + len; 20 for(int k = i + 1; k < j; k++) 21 { 22 if(dp[i][j] == 0) 23 dp[i][j] = dp[i][k] + dp[k][j] + num[i] * num[k] * num[j]; 24 else 25 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j] + num[i] * num[k] * num[j]); 26 } 27 } 28 } 29 cout << dp[0][n - 1] << endl; 30 return 0; 31 }