传送门
题意:给定整数数列a,b,长度都为n 。可以选择a的一个子区间反转,使得Σa[i]*b[i]
最大。 输出这个最大值。
(1<=n<=5000)
分析:题目看上去像区间dp,但是如果暴力求dp[i][j]翻转后的乘积值的话,时间是O(n³),肯定不行 。
但是我们注意,如果我们算出了2 3 4翻转后的乘积,是可以O(1)转移到1 2 3 4 5翻转的乘积的,因为后者比前者多a[1]*b[5]+a[5]*b[1]-a[1]*b[1]-a[5]*b[5]
。
所以可以在O(N²)完成。
- 转移方程:
dp[i][j] = dp[i+1][j-1] + a[i]*b[j]+a[j]*b[i]-a[i]*b[i]-a[j]*b[j] ( j-i>1)
dp[i][j] = dp[i][i] + a[i]*b[j]+a[j]*b[i]-a[i]*b[i]-a[j]*b[j] (j-i==1)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 5e3+10;
const int mx = 40;
const int mod = 1e9+7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int INF = 1e9+7;
int n;
ll a[maxn],b[maxn];
ll dp[maxn][maxn];//[i,j]逆转 乘积和 dp[i][j]=dp[i+1][j-1]+a[i]*b[j]+a[j]*b[i]-a[i][b[i]-a[j]b[j]
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%I64d",a+i);
ll ans=0;
for(int i=1;i<=n;i++)
{
scanf("%I64d",b+i);
ans+=a[i]*b[i];
}
for(int i=1;i<=n;i++)
{
dp[i][i]=ans;
}
for(int r=2;r<=n;r++)
{
for(int i=1;i<=n-r+1;i++)
{
int j=i+r-1;
if(r==2) dp[i][j]=ans-a[i]*b[i]-a[j]*b[j]+a[i]*b[j]+a[j]*b[i];
else
{
dp[i][j]=dp[i+1][j-1]-a[i]*b[i]-a[j]*b[j]+a[i]*b[j]+a[j]*b[i];
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
ans=max(ans,dp[i][j]);
}
}
cout<<ans;
return 0;
}