【二分答案 && dp】 Bare Minimum Difference

【二分答案 && dp】 Bare Minimum Difference_第1张图片


分析:

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

你可能感兴趣的:(题解,二分答案,动态规划,算法)