- 本来,小烈可以按 的顺序一次给每个顾客上菜,但是,聪明的小烈通过观察发现,每个顾客都有一个开心值 ,离厨房最近的为 ,然后依次为 。若小烈给第 位顾客上菜前刚刚为第 位顾客上菜,则第 位就会高兴,产生高兴指数 。这样,如果小烈按一定的方式调整上菜顺序,可以得到更高的高兴指数。现在小烈想知道用某一方法可达到的 位顾客高兴指数之和的最大值。因为顾客越高兴,给小烈的小费越多。第一位上菜的顾客不产生高兴值。
- 第一行一个整数 ,顾客的数目。
- 第二行 个数,第 个数表示第 位顾客的开心值。各个数字用空格隔开。
- 样例解释:从左往右上 的菜,再上 的菜,高兴值是 ,从右往左走回来的时候上 的菜,高兴值是 ,总的高兴值就是 。
- 对于 的数据 ;
- 对于 的数据 ;
- 对于 的数据 ;
- 所有数字小于(含结果) ;
题解
- 这道题用动态规划解决。
- 我们定义 (f[i][j]) 表示去程走到 (i),回程走到 (j) 时可以取得的最高开心指数,但这种状态的定义不能确定在 i 之前的位置有哪些位置是尚未送菜的,同样的问题也出现在 (j) 之后的位置中。这就好像 我们才做的 NOIP08 的《方格取数》,如果两个进程的起点不一样,我们无法确定哪个方格已经被取过了,哪个还没有。
- 这就启发我们将题目所描述的情形变成两个小烈同时从左边往右边走,容易看出,这样转化是等价的。同时,我们需要注意的是每个位置都必须送到,而且只能有两个小烈里面的其中一个送到。这样考虑之后,状态之间的转移关系就很显然了。
- (f[i][j]) 表示小烈 (a) 和小烈 (b) 分别走到了 (i,j) 位置,同时记 (m=max(x1,x2)),对于小于 (m) 的每一个位置都已经被送到。那么,在 (f[i][j]) 这一状态下,需要决定第 (m+1) 个人由谁来送菜。显然这是一个多阶段决策问题,(m) 就是阶段的标识。可以用动态规划求解。
- 同时,可以发现,在第 (m) 个阶段通过决策转移到 (m+1) 个阶段是,每一个决定(小烈 (a) 来送还是小烈 (b) 来送)都对应一个状态为:(f[m+1][j]) 和 (f[i][m+1]) 。
- 因为在送的过程中,每个位置都需要送到,而且两个小烈都是一样的,所以,(f[i][j]==f[j][i]) 。这个性质很重要,大家一定要好好领悟领悟。
- 这样,我们让小烈 a 始终在前面,小烈 b 在后面,即,(f[i][j]) 保证 (i>j) 。
- 转移方程:
f[i + 1][j] = max(f[i + 1][j], f[i][j] + a[i] * a[i + 1]); // i+1 由 i 转移过来
f[i + 1][i] = max(f[i + 1][i], f[i][j] + a[j] * a[i + 1]);
// i+1 由 j 转移过来,此时另外一个小烈必然在i 比较难理解,一定要先理解红色的地方,然后保证第一维大于第二维。
code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2500;
int a[maxn], f[maxn][maxn];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++)
for (int j = 0; j < i; j++) {
f[i + 1][j] = max(f[i + 1][j], f[i][j] + a[i] * a[i + 1]);
f[i + 1][i] = max(f[i + 1][i], f[i][j] + a[j] * a[i + 1]);
}
int ans = 0;
for (int i = 0; i < n; i++) ans = max(ans, f[n][i] + a[n] * a[i]);
cout << ans << endl;
return 0;
}