C. Keshi Is Throwing a Party(二分答案),最/佳牛围栏,average。

题目链接


题意:

一共n个人,第 i 个人的财富度为 i,每个人有两个属性 ai 和 bi,分别表示最多能够容忍的比自己富和比自己穷的人数。
问,最多能够选多少人,使得所有人的要求都可以满足。

思路:

二分答案:二分选择的人数。
check
从前往后遍历所有人,判断拿了这个人是否能拿够mid个。即:
如果已经拿了的人数 cnt 不超过这个人的 bi,并且还没拿的 mid-cnt-1 个比其富有的人不超过 ai,那么这个人就是可以拿的。
最后判断拿的人数 cnt 是否大于等于 mid 个。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m;
PII a[N];

bool check(int mid)
{
    
    
	int cnt=0;
	for(int i=1;i<=n;i++)
	{
    
    
		if(a[i].fi>=mid-cnt-1&&a[i].se>=cnt) cnt++;
		if(cnt>=mid) return 1;
	}
	return 0;
}

int main(){
    
    
	Ios;
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i].fi>>a[i].se;
		
		int l=0,r=n;
		while(l<r)
		{
    
    
			int mid=l+r+1>>1;
			if(check(mid)) l=mid;
			else r=mid-1;
		}
		cout<<l<<"\n";
	}
	
	return 0;
}

这道题的check除了最后,中间还用到了mid来作判断,类似于之前的 跳石头 那道题,但是又不太一样。
很奇很怪,很巧很妙!


又翻看了之前的二分答案的一道经典题:最佳牛围栏,发现又有了新的感悟。

题意是这样的:
一共 n 个数,要从中选出连续的至少为 m 个数。问,选出的这些数的平均值最大为多少?
看到这道题的时候想,为什么是二分呢?但是如果用二分来做,确实可以。。
如果较小的平均值能够满足的话,就往大了判断;不满足就往前来。

判断一个区间的平均值是否至少为mid:
将区间中的所有值都减掉mid,判断总和是否至少为0。如果是,那就说明这一段的总和至少为mid*n,也就是平均值至少为mid。

所以判断整个区间是否存在一段子区间的平均值满足至少为mid,就可以先将所有值都减去mid,找出最大子段和是否至少为0就可以了。
这个最大子段的原平均值便至少为mid。

用这种做法,就可以使得每次check都是O(n)的复杂度。

长度至少为m,只需要保证遍历的时候对当前位置的更新延迟m个位置便可。

const int N = 100010, mod = 1e9+7;
int T, n, m;
double a[N],b[N],s[N];

bool check(double mid)
{
    
    
	for(int i=1;i<=n;i++) b[i]=a[i]-mid;
	
	double mina=2e9,sum=0;
	for(int i=1;i<=n;i++)
	{
    
    
		s[i]=s[i-1]+b[i];
		
		if(i>=m){
    
    
			mina=min(mina,s[i-m]);
			if(s[i]-mina>=0) return 1;
		}
	}
	return 0;
}

int main(){
    
    
	cin>>n>>m;
	
	for(int i=1;i<=n;i++) cin>>a[i];
	
	double l=0,r=1e9;
	while(r-l>1e-5)
	{
    
    
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	cout<<(int)(r*1000);
	
	return 0;
}

之前暑假多校做过一个最佳牛围栏的改编题,加了点思维。

题目链接

题意:

有一个 n*m 的矩形 W W W,由下述方法构造:
W i , j = a i + b j , ∀ i ∈ [ 1 , n ] , ∀ j ∈ [ 1 , m ] W_{i,j}=a_{i}+b_{j},\forall i\in [1,n],\forall j\in [1,m] Wi,j=ai+bji[1,n],j[1,m].

扫描二维码关注公众号,回复: 13523741 查看本文章

​现要找到长至少为x,宽至少为y的子矩阵,使其所有元素平均值最大。
输出最大值。

思路:

对于第一行,其总和为
W 11 + W 12 + W 13 + . . . + W 1 m W_{11}+W_{12}+W_{13}+...+W_{1m} W11+W12+W13+...+W1m
a 1 + b 1 + a 1 + b 2 + a 1 + b 3 + . . . + a 1 + b m a_1+b_1+a_1+b_2+a_1+b_3+...+a_1+b_m a1+b1+a1+b2+a1+b3+...+a1+bm
m ∗ a 1 + b 1 + b 2 + . . . + b m m*a_1 + b_1+b_2+...+b_m ma1+b1+b2+...+bm
其平均值为 a 1 a_1 a1bi之和的平均值
为了使得第一行的平均值最大,那么就是要让 b 1 + b 2 + . . . + b m b_1+b_2+...+b_m b1+b2+...+bm 的平均值最大。
如此,便是和最佳牛围栏一样,选择一段连续的平均值最大的子区间,便确定了 bi 的选择。
同理,为了使得所有列之和的平均值最大,就是让 a 1 + a 2 + . . . + a m a_1+a_2+...+a_m a1+a2+...+am 的平均值最大。
矩阵中的所有元素的最大平均值便是 ai的最大平均值 + bi的最大平均值。

所以这道题就转化为了两道最佳牛围栏。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m;
double a[N],b[N];
double s[N],t[N];

bool check(double a[],int n,int x,double mid)
{
    
    
	for(int i=1;i<=n;i++) t[i]=a[i]-mid;
	
	double mina=1e9,ans=-1;
	for(int i=1;i<=n;i++)
	{
    
    
		s[i]=s[i-1]+t[i];
		if(i>=x)
		{
    
    
			mina=min(mina,s[i-x]);
			ans=max(ans,s[i]-mina);
		}
	}
	if(ans>=0) return 1;
	return 0;
}

int main(){
    
    
	int x,y;
	cin>>n>>m>>x>>y;
	
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++) cin>>b[i];
	
	double l=0,r=1e9;
	while(r-l>1e-9)
	{
    
    
		double mid=(l+r)/2;
		if(check(a,n,x,mid)) l=mid;
		else r=mid;
	}
	double ans=r;
	
	l=0,r=1e9;
	while(r-l>1e-9)
	{
    
    
		double mid=(l+r)/2;
		if(check(b,m,y,mid)) l=mid;
		else r=mid;
	}
	printf("%.10f",ans+r);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/121544430