【Ybt OJ】[基础算法 第3章]二分算法

「 「 基础算法 」 」 3 3 3章 二分算法
目录:

A.数列分段
B.防具布置
C.最大均值

A . A. A. 例题 1 1 1 数列分段

洛谷 l i n k link link
在这里插入图片描述

分析:

可以省去前缀和的空间 那么考虑一个贪心二分
就加 不能就累计段数 最后判断段数小不小于 m m m即可.
前缀和 O ( n ) O(n) O(n)复杂度

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,m,a[N],l,r;
bool check(int x)
{
    
    
	int cnt=1,sum=0;
	for(int i=1;i<=n;i++)
	{
    
    
		if(sum+a[i]<=x) sum+=a[i];  //贪心
		else cnt++,sum=a[i];
	}
	return cnt<=m;
}
void work()
{
    
    
	while(l<r)  //二分
	{
    
    
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
}
int main(){
    
    
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
    
    
		scanf("%d",&a[i]);
		l=max(l,a[i]);r+=a[i];
	}
	work(); 
	printf("%d",l);
	
	return 0;
} 

B . B. B. 例题 2 2 2 防具布置

在这里插入图片描述
在这里插入图片描述

分析:

a w a i awa_i awai 表示 0 0 0~ i i i的位置一共有多少防具 只用分类讨论一下 复杂度 O ( n ) O(n) O(n)
a w a i awa_i awai是可以求出来的
如果 2 31 − 1 2^{31}-1 2311为偶数 那么整条防线都没有破绽
如果破绽位置 k k k 那么 k k k上有奇数个防具 其他位置都是偶数防具
a w a k awa_k awak为奇数 a n s ans ans必定在 m i d mid mid m i d mid mid之前 所以 r = m i d r=mid r=mid
a w a k awa_k awak为偶数 a n s ans ans就在 m i d mid mid之后 所以 l = m i d + 1 l=mid+1 l=mid+1

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define reg register 
using namespace std;
typedef long long ll;
struct node{
    
    
	ll s,e,d;
}a[200005];
ll T,n,MAX=(1<<31)-1,l,r,mid;
ll awa(ll x)
{
    
    
	ll ans=0;
	for(reg int i=1;i<=n;i++)
	{
    
    
		if(a[i].s>x) continue;
		ans+=(min(x,a[i].e)-a[i].s)/a[i].d+1;  //求防具数量
	}
	return ans;
}
void print()
{
    
    
	if(awa(MAX)%2==0){
    
    
		puts("There's no weakness.");  //没有破绽
		return;
	}else{
    
    
		printf("%d %d\n",l,awa(l)-awa(l-1));
		return;
	}
}
void work()
{
    
    
	l=0,r=MAX;
	while(l<=r)
	{
    
    
		mid=(l+r)>>1;
		if(awa(mid)%2==0) l=mid+1;  //二分
		else r=mid-1;
	}
}
int main(){
    
    
	scanf("%lld",&T);
	while(T--)
	{
    
    
		scanf("%lld",&n);
		for(reg int i=1;i<=n;i++)
			scanf("%lld%lld%lld",&a[i].s,&a[i].e,&a[i].d);
		work();
		print();
	}
	
	return 0;
} 

C . C. C. 例题 3 3 3 最大均值

在这里插入图片描述

分析:

二分平均值 求出全部数减去平均值的前缀和
小数二分就直接每次加上一个很小的小数即可
然后就更新 m a x max max前缀和 和每次与新 s u m i − L sum_{i-L} sumiL m i n min min

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define reg register 
using namespace std;
typedef long long ll;
typedef double db;
const int N=1e5+5;
const db add=1e-6;
int ans,n,k;
db a[N],sum[N],l,r,mid;
bool check(db x)
{
    
    
	db minn=1e8,res=-1e8;
	memset(sum,0,sizeof(sum));
	for(reg int i=1;i<=n;i++)
		sum[i]=sum[i-1]+a[i]-x;  //前缀和
	for(reg int i=k;i<=n;i++)
	{
    
    
		minn=min(minn,sum[i-k]);
		res=max(res,sum[i]-minn);  //更新答案
	}
	return res>=0;
}
int main(){
    
    
	scanf("%d%d",&n,&k);
	for(reg int i=1;i<=n;i++)
		scanf("%lf",&a[i]);
	l=0;r=2000;
	while(l+add<r)  //二分
	{
    
    
		db mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	ans=int(r*1000);
	printf("%d",ans);
	
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/112131442
今日推荐