D. Maximum Sum of Products(前缀和 + 区间dp)

1.题目介绍

题目传送门
翻译后

D最大乘积和

每测试2秒的时间限制

每测试256兆字节的内存限制

输入标准输入

输出标准输出

给出了两个长度为n的整数数组a和b。

最多可以反转阵列a的一个子阵列(连续子段)。

您的任务是反转这样一个子数组,该子数组的和∑i=1nai⋅bi最大化。

输入


第一行包含一个整数n(1)≤N≤5000).

第二行包含n个整数a1,a2,…,an(1≤a[i]107).

第三行包含n个整数b1,b2,…,bn(1≤b[i]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]21+33+22+34+12=29.

在第二个示例中,不需要使用反向操作。132+374=174.

在第三个示例中,可以反转子阵列[3,5]。然后a=[1,8,3,6,7,6]15+89+36+68+78+66=235.


2.解题思路

很明显的区间dp,枚举翻转区间的长度len[2 -> n],再枚举起点s[1, n - len +1], 最后进行计算。
先计算出未反转前的前缀和,再对翻转区间进行区间dp
设当前翻转区间[i, j], 它是由上一区间[i + 1, j - 1] + 上区间端点 i, j翻转转移而来的,从而设计出状态转移方程

c[i][j] = c[i + 1][j - 1] + a[i] * b[j] + a[j] * b[i];

3.参考代码

#include<bits/stdc++.h>
#define x first
#define y second
#define rep(i, a, b) for (i = (a); i <= (b); ++i)
using namespace std;
typedef pair<int, int> P;
typedef long long ll;
const int N = 5010;
const int mod = 1e9 + 7;

int n;
ll a[N], b[N];
ll dp[N]; // 统计前缀和
ll c[N][N]; // 统计翻转后的区间和

int main()
{
    scanf("%d", &n);
    ll ans = 0;
    for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
    for (int i = 1; i <= n; i ++ ) {
        scanf("%lld", &b[i]);
        //初始化
        dp[i] = dp[i - 1] + a[i] * b[i];
        c[i][i] = a[i] * b[i];
    }
    ans = dp[n];
    //暴力做法 O(n^3)
    /*
      for (int len = 2; len <= n; ++len) { // 枚举翻转区间长度
        for (int i = 1; i <= n - len + 1; ++i) { // 枚举起点
            int j = i + len - 1; // 终点
            ll u = dp[i - 1] + dp[n] - dp[j]; //翻转区间以外的总和
    
            //cout << u<< endl;
            //依次枚举翻转区间内所有的点
            for (int k = i, v = j; k <= j; ++k, --v) {
                u += a[k] * b[v];
            }
            ans = max(ans, u);
        }
    } */
    //区间优化
    for (int len = 2; len <= n; ++len) {
        for (int i = 1; i <= n - len + 1; ++i) {
            int j = i + len - 1;
            // 当前的翻转区间[i, j]是由上一翻转区间[i + 1, j - 1] + 当前区间端点翻转 (a[i] * b[j] + a[j] * b[i])
            c[i][j] = c[i + 1][j - 1] + a[i] * b[j] + a[j] * b[i];
            ll u = dp[i - 1] + dp[n] - dp[j];
    
            //cout << u<< endl;
            u += c[i][j];
            ans = max(ans, u); //统计最大值
        }
    }
    cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值