分析:
首先我们能够得知这个优秀值具有单调性:
如果一个优秀值 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 longconst 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;
}