Running Miles(dp)

107 阅读2分钟

Problem - D - Codeforces

原题题面

image.png

题目描述

给你一个长度为 nn 的数组,现在要求你选定一对 l,r(l<r)l,r(l<r),定义一个值为 b1+b2+b3(rl)b_1+b_2+b_3-(r-l),其中 b1,b2,b3b_1,b_2,b_3 是这段区间中最大的三个数,要求这个值最大并输出这个值。

共有 tt 组样例。

输入样例

4
5
5 1 4 2 3
4
1 1 1 1
6
9 8 7 6 5 4
7
100000000 1 100000000 1 100000000 1 100000000

输出样例

8
1
22
299999996

样例解释

在第一个样例中,我们可以选择 l,rl,r 分别为 1,51,5。这样最终值为 5+4+3(51)=85+4+3-(5-1)=8,可以证明这时最优解。

题目分析

这是一道关于 DP 的题目。

首先我们分析这个题目,题目要求我们选择一个区间,得到一个相对应的值为区间内最大的三个数与区间长度的差。我们自然可以想到,若将这三个值在数组中标注出来,则其靠左的那个和靠右的那个一定是所选择区间的左右边界。于是我们可以将 b1+b2+b3(rl)b_1+b_2+b_3-(r-l) 表示为 (b1+l)+b2+(b3r)(b_1+l)+b_2+(b_3-r),这样便可以略去边界这一条件。同时我们注意到每件物品只有选或不选两种状态,自然可以想到通过 dp 的方式解决问题。

我们定义 f[i][3]f[i][3] 表示前 ii 件物品中选择了 0,1,2,30,1,2,3 件所对应的最优解,首先将 f[0][j]f[0][j] 重置为负无穷避免其干扰答案。

转移方程为:
f[i][1]=max(f[i1][1],i+a[i]);f[i][1] = max(f[i - 1][1], i + a[i]);
f[i][2]=max(f[i1][2],f[i1][1]+a[i]);f[i][2] = max(f[i - 1][2], f[i - 1][1] + a[i]);
f[i][3]=max(f[i1][3],f[i1][2]i+a[i]);f[i][3] = max(f[i - 1][3], f[i - 1][2] - i + a[i]);

最终答案便为 f[n][3]f[n][3],时间复杂度为 O(n)O(n)

Accept代码

#include <bits/stdc++.h>

using namespace std;
const int N = 100010;
int f[N][3];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    int t; cin >> t;
    while (t --)
    {
        int n; cin >> n;
        for (int i = 0; i <= n; i ++) f[i][1] = f[i][2] = f[i][3] = 0;
        f[0][0] = f[0][1] = f[0][2] = f[0][3] = -1e9;
        vector<int> a(n + 1);
        for (int i = 1; i <= n; i ++) cin >> a[i];
        for (int i = 1; i <= n; i ++)
        {
            f[i][1] = max(f[i - 1][1], i + a[i]);
            f[i][2] = max(f[i - 1][2], f[i - 1][1] + a[i]);
            f[i][3] = max(f[i - 1][3], f[i - 1][2] - i + a[i]);
        }
        cout << f[n][3] << "\n";
    }
    return 0;
}