题目大意:c++
约翰常常给产奶量高的奶牛发特殊津贴,因而很快奶牛们拥有了大笔不知该怎么花的钱.为此,约翰购置了 \(N(1 \le N \le 2000)\) 份美味的零食来卖给奶牛们.天天约翰售出一份零食.固然约翰但愿这些零食所有售出后能获得最大的收益.这些零食有如下这些有趣的特性:spa
\(V_i\) 是从盒子顶端往下的第i份零食的初始价值.约翰告诉了你全部零食的初始价值,并但愿你能帮他计算一下,在这些零食全被卖出后,他最多能获得多少钱.code
解题思路:递归
咱们定义状态 \(f[L][R]\) 为将区间 \([L,R]\) 依次去空能可以得到最多的钱。ci
那么咱们能够发现咱们的答案就是 \(f[1][n]\) ,那么怎么求解 \(f[1][n]\) 呢?先不急,听我细细道来~it
假设咱们如今要求解 \(f[L][R]\) ,那么咱们能够发现,对于区间 \([L,R]\) ,咱们取走一份零食的方案只有两种:for循环
对于区间 \([L,R]\) ,首先咱们要肯定咱们取走的零食(不管是方案一仍是方案二)是第几份取走的零食?class
咱们能够发现,\([L,R]\) 区间的左边有 \(L-1\) 份零食在以前被取走了,右边有 \(n-R\) 份零食在以前被取走了,因此咱们如今取的零食是第 \(L-1 + n-R + 1 = n+L-R\) 份。搜索
因此采用第一种方案可以得到的最多的钱是循环
\[f[L+1][R] + V_l \times (n+L-R)\]
采用第二种方案可以得到的最多的钱是
\[f[L][R-1] + V_R \times (n+L-R)\]
那咱们如今要求解的状态 \(f[L][R]\) 应该是两种方案的较大值,因此咱们能够获得最终的状态转移方程以下:
\[f[L][R] = \max(f[L+1][R] + V_l \times (n+L-R), f[L][R-1] + V_R \times (n+L-R))\]
固然,还须要注意的状况是咱们的边界条件,即:区间长度为 \(1\) 时的状况,此时,对于全部的区间 \([i,i]\) ,第 \(i\) 份零食都是最后取走的(即第 \(n\) 份被取走的),因此
\[f[i][i] = V_i \times n\]
基于上面的推导,咱们能够经过记忆化搜索的形式实现咱们的主要代码:
int dfs(int L, int R) { if (f[L][R]) // 记忆化操做 return f[L][R]; if (L == R) // 边界条件 return V[L] * n; return f[L][R] = max(dfs(L+1, R)+V[L]*(n+L-R), dfs(L, R-1)+V[R]*(n+L-R)); }
记忆化搜索的思想仍是很是对应咱们人脑的思考方式的,咱们能够发现,这个程序主要分为三部分:
首先咱们的 dfs(L,R)
就是为了返回 \(f[L][R]\),因此它:
使用记忆化搜索的完整代码以下:
#include <bits/stdc++.h> using namespace std; const int maxn = 2020; int n, V[maxn], f[maxn][maxn]; int dfs(int L, int R) { if (f[L][R]) // 记忆化操做 return f[L][R]; if (L == R) // 边界条件 return V[L] * n; return f[L][R] = max(dfs(L+1, R)+V[L]*(n+L-R), dfs(L, R-1)+V[R]*(n+L-R)); } int main() { cin >> n; for (int i = 1; i <= n; i ++) cin >> V[i]; cout << dfs(1, n) << endl; return 0; }
咱们也能够采用通常形式来解决这个问题(通常形式和记忆化搜索形式的思路都是同样的,只不过一个是直接for循环顺着来,另外一个是递归着来,要注意区分和类比)。
咱们能够发现,大区间(即区间长度较大的区间)对应的状态都是经过小区间(即区间长度较小的区间)对应的状态推导出来的,因此咱们只要从小到大遍历区间长度,再遍历区间左坐标,计算对应状态便可。
主要代码以下:
for (int l = 1; l <= n; l ++) { // 从小到大遍历区间长度l for (int i = 1; i+l-1 <= n; i ++) { // 遍历区间左边界i int j = i+l-1; // 经过左边界i和区间长度l得到区间右边界j if (l == 1) f[i][j] = V[i]*n; // 边界条件直接返回结果 else f[i][j] = max(f[i+1][j] + V[i]*(n+i-j), f[i][j-1] + V[j]*(n+i-j)); // 不然,经过状态转移方程推导 } }
通常形式的完整实现代码以下:
#include <bits/stdc++.h> using namespace std; const int maxn = 2020; int n, V[maxn], f[maxn][maxn]; int main() { cin >> n; for (int i = 1; i <= n; i ++) cin >> V[i]; for (int l = 1; l <= n; l ++) { // 从小到大遍历区间长度l for (int i = 1; i+l-1 <= n; i ++) { // 遍历区间左边界i int j = i+l-1; // 经过左边界i和区间长度l得到区间右边界j if (l == 1) f[i][j] = V[i]*n; // 边界条件直接返回结果 else f[i][j] = max(f[i+1][j] + V[i]*(n+i-j), f[i][j-1] + V[j]*(n+i-j)); // 不然,经过状态转移方程推导 } } cout << f[1][n] << endl; return 0; }