添加链接描述例如二分中如何取到最小时间或者最小值,一般要考虑到用二分。
二分模板
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
//两个模板都可以用 只不过有的时候下面才能出正确答案
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid-1;
else l=mid+1;
}
check函数用于判断是否符合所需的答案
P1843 奶牛晒衣服
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=5e5+5;
ll n,m,len=0,tot=0,a[N],x,y;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
bool check(ll k)
{
ll m=k;
fr(i,n)
if(m*x<a[i])
k-=ceil((double)(a[i]-m*x)/y); ///找到让所有衣服都能干的时间
return k>=0;
}
int main()
{
n=read();x=read();y=read();
fr(i,n) a[i]=read();
ll l=1,r=1e8;
while(l<r)
{
ll mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l;
return 0;
}
又是看题解 写题的一天
P2390 地标访问 这是一道二分枚举题目,因为路标是线性分布的所以没有必要去dfs搜索(在这上面想了很长时间没有头绪),有以下三种情况判断:
- 一直向右边走
- 一直往左边走
- 先往一边走然后回头走另一边
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=2e5+5;
ll n,m,len,tot=0,x,y,a[N],sum=0,b[N],pos;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int check(int x)
{
for(int i=x;i<=n;i++)
{
int l=i-x+1;
if(a[l]>=0)
if(a[i]<=m) return 1;
if(a[i]<=0)
if(-a[l]<=m) return 1;
if(a[l]<=0&&a[i]>=0)
if (min(-a[l],a[i])+a[i]-a[l]<=m) return 1;
}
return 0;
}
int main()
{
m=read(),n=read();
fr(i,n) a[i]=read();
sort(a+1,a+n+1);
ll l=-1,r=n+1;
while(l+1<r)
{
ll mid=(l+r)>>1;
if(check(mid)) l=mid;
else r=mid;
}
cout<<l<<endl;
return 0;
}
0/1分数规划
1)知识点
这道题就是一道简单的01分数规划,再运用二分。
2)看题目
这道题要求单位价值(题目中的总价值/总花费),我们假设单位价值为x,我们就可以知道,x = Σv / Σc。
3)算法分析
二分是本题的答案:假设一个数L 如果有若干对 ai/bi 大于L则说明L比最大值要小,而如果对于所有的 ai/bi 都要比L要小 则L比最大值要大 。由此得出L具有单调性通过二分:得到L 选出K对ai和bi使得 Σai - Σbi * L > 0即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=2e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],b[N];
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
bool check(double x)
{
double c[N],sum=0;
fr(i,n) c[i]=a[i]-b[i]*x;
sort(c+1,c+n+1,greater<double>() );
fr(i,m) sum+=c[i];
return sum>=0;
}
int main()
{
int t=read();
while(t--)
{
n=read(),m=read();
fr(i,n) b[i]=read(),a[i]=read();
double l=0,r=INF;
for(int i=1;i<=100;i++) ///保证高精度
{
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
cout<<(int)l<<endl;
}
return 0;
}
P2678 [NOIP2015 提高组] 跳石头
本题思路大致如下:枚举 a[i]-a[j]<mid 则ans加一 否则j=i
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=3e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],b[N],len;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
bool check(int x)
{
int sum=0,j=0;
fr(i,n+1)
if(a[i]-a[j]<x) sum++; ///线性枚举
else j=i;
return sum<=m;
}
int main()
{
len=read(),n=read(),m=read();
fr(i,n) a[i]=read();
sort(a+1,a+n+1);
a[n+1]=len;
int l=0,r=INF;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid)) l=mid+1;
else r=mid-1;
}
cout<<l-1<<endl;
return 0;
}
P3853 [TJOI2007]路标设置
一道思路简单的二分题:
- 二分出最大距离x 判断当前位置路标位置 j 是否和后一个路标距离小于x
- 若距离大于x则在 j+x 的地方放置路标 循环直到距离小于x
- 若距离小于x则到下一个路标的位置
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=3e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],b[N],len;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
bool check(int x)
{
int sum=0,j=0;
fr(i,n)
{
while(j+x<a[i]) j+=x,sum++;
j=a[i];
}
return sum<=m;
}
int main()
{
len=read(),n=read(),m=read();
fr(i,n) a[i]=read();
int l=0,r=INF;
while(l<r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}
P1281 书的复制
这题的思路是没有问题的,只是根据题目要求,第一个人写的尽量少那么就从n到1枚举。在输出答案之前应该先进行判断最好再去输出,因为我便判断边记录答案,最好导致答案出了问题。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=3e5+5,INF=0x3f3f3f3f;
int n,m,a[N],cnt=0,t=0;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
}b[N];
bool check(int x)
{
int sum=1,t=x-a[n];
for(int i=n-1;i>=1;i--)
{
if(a[i]<=t) t-=a[i];
else
{
t=x-a[i];
sum++;
}
}
return sum<=m; ///先判断再去记录答案
}
void print(int x)
{
/*cout<<x<<endl;*/
int k=x-a[n],cnt=0;
b[++cnt].r=n;
for(int i=n-1;i>=1;i--)
{
if(a[i]<=k) k-=a[i];
else
{
k=x-a[i];
b[cnt].l=i+1;
b[++cnt].r=i;
}
}
b[m].l=1;
for(int i=m;i>=1;i--) cout<<b[i].l<<" "<<b[i].r<<endl;
}
int main()
{
n=read(),m=read();
fr(i,n) a[i]=read();
ll l=0,r=INF;
while(l<=r)
{
ll mid=(l+r)/2;
if(check(mid)) r=mid-1;
else l=mid+1;
}
print(l);
return 0;
}
P1083 [NOIP2012 提高组] 借教室
思路:二分订单数量,差分若当前天数的教室小于0则r=mid(r往更小的范围寻找),否则l=mid;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef unsigned long long ull;
#define fr(i,n) for(int i=1;i<=n;i++)
const int mod = 107,N=1e6+5,INF=0x3f3f3f3f;
ll n,m,cnt=0,t=0,a[N],c[N],flag=0;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
ll l,r;
int d;
}b[N];
int check(int x)
{
memset(c,0,sizeof(c));
fr(i,x) c[b[i].l]-=b[i].d,c[b[i].r+1]+=b[i].d;
fr(i,n)
{
c[i]+=c[i-1];
int t=a[i]+c[i];
if(t<0)
{
flag=1;
return 1;
}
}
return 0;
}
int main()
{
n=read(),m=read();
fr(i,n) a[i]=read();
fr(i,m) b[i].d=read(),b[i].l=read(),b[i].r=read();
int l=1,r=m;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(flag)
{
cout<<-1<<endl;
cout<<l<<endl;
}
else
cout<<0<<endl;
return 0;
}