牛客周赛 Round 86——F小苯的线性dp
题目
题解
通过观察我们可以发现,为了最大化极差,我们需要:
最大值尽可能大:通过合并相邻元素,可以生成更大的数。
最小值尽可能小:通过不合并最小值,或者让最小值单独存在。
观察到:
执行k次合并后,数组长度为n−k。
最大极差可以通过以下方式实现:
最大值是某个连续子数组的和(通过合并相邻元素)。
最小值是某个单独的元素(不合并它)。
因此,对于每个k,我们需要:
找到一个长度为n−k的分割,其中至少一个段是一个单独的元素(最小值),另一个段是尽可能大的合并和(最大值)。
如果把a[i]作为最小值,我们可以找到a[i]前面的长度为k+1区间大小的最值和后面长度为k+1区间大小的最值,两者取大者减去a[i],就是以a[i]作为最小值的极差。
我们代码中的k表示的是合并完成后的长度而不是操作k次,这点需要注意,然后我们上面这个方面的前提条件是合并完最少也要三个数,但是我们如果执行了n-2次操作就会变成只有两个数,那就不能这么判断,需要单独拿出来判断,最后n-1次的结果固定为0。
题目中枚举的k和题目中说明的操作k次是k(代码) = k(题目) + 1的关系。
参考代码
#include<iostream>
#include<vector>
using namespace std;
#define int long long
void solve(){
    int n;
    cin >> n;
    vector<int> a(n + 1),s(n + 1);
    for(int i = 1; i <= n; i ++){
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }
    for(int k = 1; k <= n - 2; k ++){
        vector<int> pre(n + 1),nxt(n + 2);
        for(int i = 1; i <= n; i ++){
            pre[i] = max(pre[i - 1], s[i] - s[max(0LL, i - k)]);
        }
        for(int i = n; i >= 1; i --){
            nxt[i] = max(nxt[i + 1], s[min(n, i + k - 1)] - s[i - 1]);
        }
        int res = 0;
        for(int i = 1;i <= n; i ++){
            res = max(res, max(pre[i - 1], nxt[i + 1]) - a[i]);
        }
        cout << res << " ";
    }   
    cout << max(s[n] - a[1] - a[1], s[n - 1] - a[n]) << " 0" << endl;
}
signed main(){
    int _ = 1;
    cin >> _;
    while(_ --){
        solve();
    } 
    return 0;
}

                
            
        
浙公网安备 33010602011771号