题目大意
给出一个整数序列,从中依次删除元素,如果删除元素card[i],就把card[i]∗card[i−1]∗card[i+1]加到删除的代价里。求最小的删除代价。不允许删除最左和最右的元素。在删除结束后,序列只剩下两个元素。
分析
动态规划。第一眼就可以看出和矩阵乘法很像。
自顶向上考虑问题。考虑最后一次删除,设最后一次删除的元素是card[k],那么删除的代价是card[0]∗card[n−1]∗card[k]+S[0][k]+S[k][n−1],其中S[0][k]表示删除序列card[0,1,...,k]需要的代价,S[k][n−1]表示删除序列card[k,k+1,...,n−1]所需要的代价。
注意每次序列删除最终都会留下头和尾,所以是S[0][k]而不是S[0][k−1].
边界条件:需要让S[i][i+1]=0
代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <string.h>
#include <queue>
#include <set>
#include <limits.h>
using namespace std;
// multiplication puzzle
// 找一个符合条件的顺序,暴力方法是O(n!)
// 动态规划
// S[i,j] = min(S[i,k] + S[k,j] + A[i]A[k]A[j]) if j-i > 2, k >i && k <j
// S[i,j] = A[i]*A[i+1]*A[j] if j-i=2
const int MAXN = 105;
int cards[MAXN];
int S[MAXN][MAXN]; //动态规划数组,S[i][j]表示以i开头以j结尾的链的最小乘积
void dp(int n) {
for (int r = 3; r <= n; r++) {
// r是链长
for (int i = 0; i < n - r + 1; i++) {
// i是链头
int j = i + r - 1; //j是链尾
cout << i << " " << j << endl;
// 接下来求S[i][j]
int min_k = -1;
for (int k = i+1; k < j; k++) {
// k是要选择剔出的数的下标
int left = S[i][k]; // S[i][i]初始化为0,其余初始化为最大值
int right = S[k][j];
int tmp_res = left + right + cards[i] * cards[k] * cards[j];
cout << left << " " << right << " " << tmp_res << endl;
if (tmp_res < S[i][j]) {
S[i][j] = tmp_res;
min_k = k;
}
}
cout << "k: " << cards[min_k] << endl;
}
}
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> cards[i];
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
S[i][j] = INT_MAX;
}
}
for (int i = 0; i < n; i++) {
S[i][i] = 0;
}
for (int i = 0; i < n - 1; i++) {
S[i][i + 1] = 0;
}
dp(n);
cout << S[0][n - 1] << endl;
return 0;
}
反思
因为一开始就觉得是矩阵乘法,所以直接开始写矩阵乘法的代码。但是因为没有考虑清楚整个问题,所以在边界上出错。实际还是对问题分析得不透彻。以后要认真分析问题,就题论题。