51nod 3180 矩阵连乘
感觉区间 dp 还是要感性理解,但好像区间有套路的,这和石子合并很像,就根据题意模拟。
这个写法的区间比较巧妙,左右同时增加,相当于滑动窗口,因为一开始花费一个是0,所以注意dp的初始化。
#include<bits/stdc++.h>
using namespace std;
int n; // 矩阵的个数
long long dp[1005][1005]; // 动态规划表,dp[i][j]表示从矩阵i到矩阵j的最小乘法次数
long long a[1005]; // 存储矩阵的维度,a[i]表示第i个矩阵的行数,a[i+1]表示它的列数
int main(){
ios::sync_with_stdio(false);
cin >> n; // 输入矩阵的个数(注意:实际输入的a数组长度是n+1,因为矩阵个数n需要n+1个维度来描述)
memset(dp, 0, sizeof dp); // 初始化动态规划表,初始时所有乘法次数都为0
// 读取n+1个值,用于表示n个矩阵的维度
for(int i = 0; i <= n; i++){
cin >> a[i]; // 输入矩阵的维度信息
}
// 动态规划部分,计算最小乘法次数
for(int len = 2; len <= n; len++){ // len是当前处理的矩阵子链长度,从2个矩阵开始
int r = len; // 初始化右端点
for(int j = 1; r <= n; j++, r++){ // j是左端点,r是右端点,遍历所有可能的子链区间[j, r]
long long mi = 1e18; // 初始化mi为一个非常大的数,相当于无穷大
for(int k = j; k < r; k++){ // 枚举分割点k,将矩阵链[j, r]分成[j, k]和[k+1, r]两部分
// 计算分割成两部分的代价,再加上这两部分相乘的代价
mi = min(mi, dp[j][k] + dp[k+1][r] + a[j-1] * a[k] * a[r]);
}
dp[j][r] = mi; // 保存子链[j, r]的最小乘法次数
}
}
cout << dp[1][n]; // 输出从矩阵1到矩阵n的最小乘法次数
return 0;
}