分析:
首先我们能够得知这个优秀值具有单调性:
如果一个优秀值
x
1
x1
x1能够满足题目要求,那么任何
x
(
x
>
x
1
)
x(x>x1)
x(x>x1)显然都能符合要求
基于这一特性,我们想到二分答案
直接二分这个答案好像难以维护。
怎么办呢?
我们观察区间的个数
我们注意到对于整个序列来说,子区间的个数最多只有
n
2
n^2
n2个,也就是说对于这道题而言有效的数值只有
n
2
n^2
n2个。
进一步分析我们发现这道题我们只需要关注最小值和最大值,所以每个区间的和只要介于最小值和最大值之间就可以
于是我们达到一下的二分思路:
n
2
n^2
n2枚举所有可能得最小值,而后二分我们的答案,得出我们的最大值
这样我们就得到了合法区间的范围
那么如何check呢?
如果从分割区间的方式出发,这道题很难进行维护,因为分割的方式多样,每一次不同的分割都会产生不同的结果
但是我们并不需要求出具体的分割方式,我们只需要去检验当前分割方式是否可行即可。
于是我们设
f
[
i
]
f[i]
f[i]表示到第i个数为止是否能分割出合法的区间
考虑如何转移:
f
[
i
]
∣
=
(
f
[
j
]
&
&
l
<
=
f
[
i
]
−
f
[
j
]
&
&
f
[
i
]
−
f
[
j
]
<
=
r
)
f[i]|=(f[j]\&\&l<=f[i]-f[j]\&\&f[i]-f[j]<=r)
f[i]∣=(f[j]&&l<=f[i]−f[j]&&f[i]−f[j]<=r)
最后返回 f [ n ] f[n] f[n]即可
同时注意到分割区间至少分分割出两个区间,所以我们只需要把一个区间的可能性判掉就行了
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 101;
int n;
int a[N],s[N];
int b[N*N],len;
bool f[N];
bool Check(int l,int x){
int r = l+x;
for (int i = 1; i <= n; i++) f[i] = 0;
for (int i = 1; i <= n; i++) if (l <= s[i] && s[i] <= r) f[i] = 1;
f[n] = 0;
int st = n;
for (int i = 1; i <= n; i++)if (f[i]) {st = i;break;}
if (st == n) return 0;
for (int i = st+1; i <= n; i++)
for (int j = st; j < i; j++){
if (f[i] == 1) break;
f[i] = (f[j] && l <= s[i]-s[j] && s[i]-s[j] <= r);
}
if (f[n]) return 1;
return 0;
}
signed main(){
scanf("%lld",&n);
for (int i = 1; i <= n; i++)
scanf("%lld",&a[i]) , s[i] = s[i-1] + a[i];
for (int i = 1; i <= n; i++)
for (int j = i; j <= n ;j++){
if (i == 1 && j == n) continue;
b[++len] = s[j]-s[i-1];
}
sort(b+1,b+len+1);
int l = 0 , r = 0;
for (int i = 1; i <= n; i++) r+=a[i];
while (l+1<r){
int Mid = l+r>>1; bool ff = 0;
for (int minn = 1; minn <= len; minn++)
if (Check(b[minn],Mid)){ff = 1;break;}
if (ff) r = Mid; else l = Mid;
}
bool ff = 0;
for (int minn = 1; minn <= len; minn++)
if (Check(b[minn],l)){ff = 1;break;}
if (ff) cout<<l;else cout<<r;
return 0;
}