题目描述
刚刚今天去游乐场玩,发现了一个新的游戏项目,游戏是这样的,场上一共有 n 个气球,它们的编号是0到n-1,然后每个气球上还有一个数字,我们使用数组nums来保存这些数字。
现在游戏要求刚刚戳破所有的气球。每当刚刚戳破一个气球i时,刚刚可以获得nums[left] * nums[i] * nums[right]个积分。这里的left和right指的是和i相邻的两个气球的序号。(注意每当刚刚戳破了气球i后,气球left和气球right就变成了相邻的气球。)
求所能获得积分的最大值。
输入数据
输入中有若干组测试样例,第一行为一个正整数T(T≤1000),表示测试样例组数。每组测试样例包含2部分: 第一部分有一行,包含1个正整数n(0≤n≤500),第二部分为一行,有n个数,第i个数表示num[i],(0≤num[i]≤100)。
输出数据
对每组测试数据,单独输出一行答案,表示最大的积分值。
#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
#define N 100000+5
const double eps = 1e-15;
int n, num[505];
long long dp[505][505];//dp[i][j]: 第i~j个气球组合的最大分值
int main() {
int T;
cin >> T;
while(T--) {
memset(num, 0, sizeof(num));
memset(dp, 0, sizeof(dp));
cin >> n;
for (int i = 1; i <= n; i++)
cin >> num[i];
if (n == 0) {
cout << 0 << endl;
continue;
}
num[0] = 1, num[n+1] = 1;
for (int i = 1; i <= n; i++)
dp[i][i] = num[i-1] * num[i] * num[i+1];
for (int len = 1; len <= n-1; len++)
for (int i = 1; i <= n; i++) {
int j = i + len;
if (j > n) break;
//枚举这一段最后一个扎爆的气球
long long max_num = -1;
for (int k = i; k <= j; k++) {
max_num = max(max_num, dp[i][k - 1] + dp[k + 1][j] + num[i - 1] * num[k] * num[j + 1]);
}
dp[i][j] = max_num;
}
cout << dp[1][n] << endl;
}
return 0;
}
总结:
最开始思路错了,转移方程如果设计为max(dp[i][k - 1] + dp[k + 1][j] + num[k - 1] * num[k] * num[k + 1]),这样会有一个问题,我们把k扎爆了,[i, k - 1]这一段我们扎k-1的话,它右边的那个气球是谁我们就不知道了。所以要枚举最后一个扎爆的气球,加上num[i - 1] * num[k] * num[j + 1]。状态计算的过程要从[i, j]的长度由小到大进行枚举,最后的dp矩阵是一个三角矩阵(i>j全0)。结果还要注意对n==0特判一下。