题目
小苯的线性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;
}