题目链接
题意:
一共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+bj,∀i∈[1,n],∀j∈[1,m].
现要找到长至少为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 m∗a1+b1+b2+...+bm
其平均值为 a 1 a_1 a1 加 bi之和的平均值
。
为了使得第一行的平均值最大,那么就是要让 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;
}