题目:
有n个数,每次从左边或右边取一个,直到取完。每取一个数会有一个分数,问总分最大是多少?
分数计算如下:
- 第一次拿的分数是数字本身。
- 随后每次得到的分数是这个数与之前已取数的极差的乘积。
分析:
动态规划问题,我们可以用dp[i][j]表示剩余序列为第 i 个数到第 j 个数时已经获得的最大分数,可以通过 dp[i−1][j] 和 dp[i][j+1] 转移过来,需要注意边界情况,同时我们需要维护已经取出的数的最大值和最小值,转移的时候进行更新即可,最后当 i=j 时,我们需要取最后一个数,并最后得到答案。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 1e3 + 5;
const double EPS = 1e-8;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n, a[MAXN], mi[MAXN][MAXN], mx[MAXN][MAXN];
ll dp[MAXN][MAXN];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
ms(mi, INF);
for (int i = 1; i <= n; i++) {
mi[i][i] = mx[i][i] = a[i];
for (int j = i + 1; j <= n; j++) {
mx[i][j] = max(mx[i][j-1], a[j]);
mi[i][j] = min(mi[i][j-1], a[j]);
}
}
ms(dp,0);
dp[1][n - 1] = a[n]; dp[2][n] = a[1];
ll ans = 0;
for (int l = n - 1; l > 1; l--) {
for (int i = 1; i <= n; i++) {
int j = i + l - 1;
if (j > n) break;
int Min = min(mi[1][i - 1], mi[j + 1][n]);
int Max = max(mx[1][i - 1], mx[j + 1][n]);
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j] + (ll)a[i] * (Max - Min));
dp[i][j - 1] = max(dp[i][j - 1], dp[i][j] + (ll)a[j] * (Max - Min));
}
}
for(int i=1;i<=n;i++){
int Min = min(mi[1][i-1],mi[i+1][n]);
int Max = max(mx[1][i-1],mx[i+1][n]);
ans = max(ans, dp[i][i] + (ll)a[i] * (Max-Min));
}
cout << ans << endl;
return 0;
}