【Educational Codeforces Round 88 (Rated for Div. 2) D】Yet Another Yet Another Task
题目链接
题目大意
给你一个长度为n的序列,先手先选择一个区间[L,R], 这个区间里面的数字, 让后手选择一个删掉。
然后计算剩余的R-L个数字的和sum(如果R-L等于0,认为和是0)。
后手总是想让这个sum的值比较低,所以它总是会选择最大的那个数字删掉。
现在让你帮先手选择一个区间,使得这个区间在被后手删掉一个数字之后, 剩余的和最大。
题解
这道题, 如果没有删掉一个数字这个操作的话,就是一个 最大连续和 问题。
不熟悉这个问题的可以看看我 另外一篇文章。
大家可能都注意到了, a数组中每个元素最大都只是30。
所以,我们可以先枚举一下最后我们选择的区间里面的最大值ma是多少。
然后,将原数组做一个改动, 把数组当中所有 大于ma的值全都改成负无穷 (其他的保持不变)。
然后在这个改动后的数组里面做最大连续子序列问题,因为比ma大的都变成负无穷了,所以肯定最后
选出来的区间没有大于ma的数字, 这样算出来的 最大连续和减去这个ma 就是最大值为ma的时候,问题的一个可能答案了。
但是大家可能会觉得,那如果选出来的区间中 没有ma这个数字(里面的数字都比ma小)怎么办? 其实这种情况下,我们因为枚举了
所有的最大值,所以在 之前的枚举中 肯定会有一次 枚举到 了这个区间的最大值的。
总结
挺好的一个思路吧, 枚举矛盾点(最大值)是多少, 因为这个关键的地方被确定了,使得问题被简化(或者说分解成很多个简单的步骤)
题解
#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;
const int N = 1e5;
int n;
int a[N+10];
int main(){
#ifdef LOCAL_DEFINE
freopen("D:\\rush.txt","r",stdin);
#endif
ios::sync_with_stdio(0),cin.tie(0);
cin >> n;
rep1(i,1,n){
cin >> a[i];
}
int ans = 0;
rep1(i,1,30){
int cur = 0;
rep1(j,1,n){
if (a[j]>i){
cur = 0;
}else{
cur = cur + a[j];
ans = max(ans,cur-i);
if (cur<0) cur = 0;
}
}
}
cout<<ans<<endl;
return 0;
}