NC14701 取数游戏2
题目
题目描述
给定两个长度为n的整数列A和B,每次你可以从A数列的左端或右端取走一个数。假设第i次取走的数为ax,则第i次取走的数的价值vi=bi⋅ax,现在希望你求出∑vi的最大值。
输入描述
第一行一个数T,表示有T组数据。
对于每组数据,第一行一个整数n,
接下来两行分别给出A数列与B数列。
输出描述
每一组数据输出一行,最大的∑vi。
示例1
输入
2
2
1 1000
2 1
5
1 3 5 2 4
1 2 3 4 5
输出
2001
52
说明
对于第二个样例,
第一次从左边取走a1,v1=a1⋅b1=1,
第二次从左边取走a2,v2=a2⋅b2=6,
第三次从右边取走a5,v3=a5⋅b3=12,
第四次从右边取走a4,v4=a4⋅b4=8,
第五次取走剩下的a3,v5=a3⋅b5=25。
总价值∑vi=1+6+12+8+25=52
备注
\(T≤10\)
\(1≤n≤10^3\)
\(1≤a_i,b_i≤10^3\)
题解
知识点:区间dp。
这类题有个很显然的特征,即每次只能选剩余的左右两端的数,可以考虑区间dp逆推整个过程,从只剩一个数作为终点逆推回原来的数列,然后取其中过程中的最大值即可。
设 \(dp[i][j]\) 为用区间 \([i,j]\) 里的数作为第 \([n-(j-i),n]\) 个数时能够到达的最大值。有转移方程:
\[dp[i][j] = \max (dp[i + 1][j] + a[i] \cdot b[n - l + 1], dp[i][j - 1] + a[j] \cdot b[n - l + 1])
\]
表示选左端点或者右端点作为第 \(n-l+1\) 个数。
时间复杂度 \(O(n^2)\)
空间复杂度 \(O(n^2)\)
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[1007], b[1007], dp[1007][1007];
bool solve() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n;i++) cin >> b[i];
for (int i = 1;i <= n;i++) dp[i][i] = a[i] * b[n];
for (int l = 2;l <= n;l++) {
for (int i = 1, j = l;j <= n;i++, j++) {
dp[i][j] = max(dp[i + 1][j] + a[i] * b[n - l + 1], dp[i][j - 1] + a[j] * b[n - l + 1]);
}
}
cout << dp[1][n] << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/16586827.html