Problem - 1519D - Codeforces
给你两个长度为n的整数数组a和b。
你最多可以扭转数组a的一个子数组(连续子段)。
你的任务是反转这样一个子数组,使其总和∑i=1nai⋅bi达到最大。
输入
第一行包含一个整数n(1≤n≤5000)。
第二行包含n个整数a1,a2,...,an(1≤ai≤107)。
第三行包含n个整数b1,b2,...,bn(1≤bi≤107)。
输出
例子
输入复制
5
2 3 2 1 3
1 3 2 4 2
输出拷贝
29
输入复制
2
13 37
2 4
输出拷贝
174
输入复制
6
1 8 7 6 3 6
5 9 6 8 8 6
输出拷贝
235
注意
在第一个例子中,你可以反转子数组[4,5]。然后a=[2,3,2,3,1],2⋅1+3⋅3+2⋅2+3⋅4+1⋅2=29。
在第二个例子中,你不需要使用反向运算。13⋅2+37⋅4=174.
在第三个例子中,你可以反转子数组[3,5]。那么a=[1,8,3,6,7,6],1⋅5+8⋅9+3⋅6+6⋅8+7⋅8+6⋅6=235。
题解:
贪心肯定是写不了这道题的,
我们如果暴力枚举所有可能区间的开头与结尾,然后再计算区间内交换的值也不行
肯定会t
那我们想想如何来优化这个过程,
首先前缀和是容易想到的,对于没有改变的区间可以快速求值
我们可以发现一些交换过的区间,可能会再次用到,
如果我们可以记录每次改变的区间,再次用到时就不用再次计算
因此我们用一个二维数组
f[l][r],代表记录的反转al~r后相乘后的值
每次dfs(i,j)我们要改变的区间,每次dfs的同时记录f[l][r]
#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
#define int long long
int a[200050];
int b[200050];
int p[200059];
int f[5005][5005];
int dfs(int l,int r)
{
if(l == r)
{
return a[l]*b[r];
}
else if(l + 1 ==r)
{
return a[l]*b[r] + a[r]*b[l];
}
if(f[l][r] != 0)
return f[l][r];
return f[l][r] = a[l]*b[r] + a[r]*b[l] +dfs(l+1,r-1);
}
void 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++)
{
p[i] = p[i-1] + a[i]*b[i];
}
int ans = 0;
for(int i = 1;i <= n;i++)
{
for(int j = i;j <= n;j++)
{
ans = max(ans,p[n] - p[j] + p[i-1] + dfs(i,j));
}
}
cout << ans<<"\n";
}
//1,0,1,1,0,1
//1 6
//3 2
//4 6
//5 3
//4 4
//5
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
int t = 1;
// cin >> t;
while(t--)
{
solve();
}
}