「 「 「基础算法 」 」 」第 3 3 3章 二分算法
目录:
A.数列分段
B.防具布置
C.最大均值
A . A. A. 例题 1 1 1 数列分段
分析:
可以省去前缀和的空间 那么考虑一个贪心 去二分
能加就加 不能就累计段数 最后判断段数小不小于 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 231−1为偶数 那么整条防线都没有破绽
如果破绽位置为 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} sumi−L取 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;
}