题意:
给你一堆卡片,每张卡片上有一个数,按顺序依次排放。每次从其中抽取一张卡片(不能在首尾取卡片),每次取卡片都会得到一个分数,分数由所取卡片与其前后两张卡片分数的乘积决定。当卡片只剩两张时结束,最终分数为每次得分的总和,求总和的最小值。
如:10 1 50 20 5
a. 若取卡片的顺序为1 20 50
第一次得分:10*1*50 = 500
第二次得分:50*20*5 = 5000
第三次得分:10*50*5 = 2500
总分: 500 + 5000 + 2500 = 8000
b. 若取卡片的顺序为 50 20 1
第一次得分:1*50*20 = 1000
第二次得分:1*20*5 = 100
第三次得分:10*1*5 = 50
总分: 1000 + 100 +50 = 1150
解题分析:
首尾两张卡片始终不会被取走,最后一张卡片的得分最小值由中间卡片的最小值确定。由最后一张卡片可以将整个序列分成两部分。最终答案为 左边卡片被取完得到的最小值 + 首卡片*最后所取卡片*尾卡片 + 右边卡片被取完得到的最小值。左右两部分的求取可以依次DP下去,不难得到DP方程:
dp[i][j] = min {dp[i][k]+arry[i]*arry[k]*arry[j]+dp[k][j], i< k < j}
dp[i][j] 表示取完卡片i ~ j 之间的所有卡片所得总分的最小值
需要注意的地方: 初始化
题解代码:
// by weikd
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 200;
int dp[maxn][maxn];
int arry[maxn];
int ans;
int main()
{
int n;
while (scanf("%d", &n) != EOF) {
memset(dp, 127, sizeof(dp)); // 初始化为最大值
for (int i = 1; i <= n; ++i) {
scanf("%d", &arry[i]); // 为了思路清晰,从ayyy[1] 开始读数据
}
for (int i = 1; i <= n-2; ++i) {
dp[i][i+2] = arry[i]*arry[i+1]*arry[i+2]; // 初始化连续的三张卡片的得分
dp[i][i+1] = 0; // 连续的两张卡片不符合题意,初始化为0
}
dp[n-1][n] = 0;
for (int i = n-2; i >= 1; --i) { // 从后往前推dp[i][j]
for (int j = i+2; j <= n; ++j) {
for (int k = i+1; k < j; k++) {
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]+arry[i]*arry[k]*arry[j]);
}
}
}
printf("%d\n", dp[1][n]); // 答案为dp[1][n]
}
return 0;
}