本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@TOC
Educational Codeforces Round 108 (Rated for Div. 2)-D. Maximum Sum of Products
传送门 Time Limit: 2 seconds Memory Limit: 256 megabytes
Problem Description
You are given two integer arrays and of length .
You can reverse at most one subarray (continuous subsegment) of the array .
Your task is to reverse such a subarray that the sum is maximized.
Input
The first line contains one integer ().
The second line contains integers ().
The third line contains integers ().
Output
Print single integer — maximum possible sum after reversing at most one subarray (continuous subsegment) of .
Sample Input
5
2 3 2 1 3
1 3 2 4 2
Sample Onput
29
Note
In the first example, you can reverse the subarray . Then and .
In the second example, you don't need to use the reverse operation. .
In the third example, you can reverse the subarray . Then and .
题目大意
给你两个数组和以及一个整数,每个数组中有个正整数。你可以选择把的一个子串翻转,使得最大。
解题思路
字串必须是连续的!
首先来想最朴素的算法:
i 从 1 到 n: j 从 i 到 n: k 从 1 到 n: 如果 i ≤ k ≤ j: ans += a[k] * b[j - k + i] 否则: ans += a[k] * b[k]
这样的话复杂度为,然而的范围是~,会超时。
简单优化一下: 因为 ~ 外面的和是一一对应的,没有翻转,故可以用一个前缀数组,其中表示。这样的话处于 ~ 外面的所有的和就可以用的时间快速得出,为。
但是, ~ 中的元素还是需要一个一个计算,这就使得总体复杂度仍为。
继续优化: 既然第三层复杂度的来源是重新计算了 ~ 中每个与相乘,那么能不能找到一种方法,使得后面的 ~ (翻转部分)的计算基于前面的 ~ 的计算结果之上呢?
于是我们想到,如果已知 ~ 的翻转后的与相乘的值,那么就能很快求出 ~ 的翻转后的与相乘的值,其中。
所以我们第一层循环是 ~ 的中间的下标,之后的同时,每次更新答案的最大值即可。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
ll a[5050],b[5050];
ll qianZui[5050];
int main()
{
int n;
cin>>n;//每个数组有n个数
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
cin>>b[i];
qianZui[0]=0;//前缀初始值
for(int i=1;i<=n;i++)
qianZui[i]=qianZui[i-1]+a[i]*b[i];//后面一个的前缀和等于前面一个的前缀和的基础上加上a[i]*b[i]
ll ans=qianZui[n];//目前答案最大值是一个都不翻转
for(int mid=1;mid<=n;mid++)//区间中间从1到n
{
ll zhongJianZheYiKuai=a[mid]*b[mid];//中间这一段 的和
for(int l=mid-1,r=mid+1;l>0&&r<=n;l--,r++)//l向左拓展的同时r向右拓展
{
zhongJianZheYiKuai+=a[l]*b[r]+a[r]*b[l];//中间这一段的和加上新拓展来的两个对应的数
ans=max(ans,zhongJianZheYiKuai+qianZui[n]-qianZui[r]+qianZui[l-1]);//更新一下答案的最大值。如果l到r翻转的话,总和就是 中间这一段+两边的和, 其中两边的和=左边的和+右边的和, 其中左边的和就是前缀l-1,右边的和是前缀n - 前缀r。
}
//注意仅仅考虑上面的话翻转区间是奇数个元素,故应加上翻转区间共有偶数个元素的情况,即中间点mid暂时不包含。
zhongJianZheYiKuai=0;//现在翻转区间为空
for(int l=mid,r=mid+1;l>0&&r<=n;l--,r++)//翻转区间中的元素个数为偶数个,区间从mid到mid+1开始往两边拓展
{
zhongJianZheYiKuai+=a[l]*b[r]+a[r]*b[l];//同上
ans=max(ans,zhongJianZheYiKuai+qianZui[n]-qianZui[r]+qianZui[l-1]);
}
}
cout<<ans<<endl;
return 0;
}
同步发文于我的CSDN,原创不易,转载请附上原文链接哦~
Tisfy:letmefly.blog.csdn.net/article/det…