二分搜索

最大化最小值

POJ 3258: River Hopscotch
同noip跳石头。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
同noip跳石头。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=50000+5;
const int INF=0x3f3f3f3f;
int a[maxn],l,n,m;
bool check(int mid){
    int sum=0,dis=0;
    for(int i=2;i<=n;i++){
        dis+=a[i]-a[i-1];
        if(dis>l>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    a[++n]=0,a[++n]=l;
    sort(a+1,a+n+1);
//  for(int i=1;i<=n;i++) D(a[i]);
    int l=0,r=INF;
    while(l>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<

POJ 3273: Monthly Expense
最小化最大值。
从左往右check即可。预处理前缀和简化运算。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
最小化最大值。 
从左往右check即可。预处理前缀和简化运算。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100000+5;
const int INF=0x3f3f3f3f;
int n,m,a[maxn],sum[maxn],maxa=0;
bool check(int mid){
//  if(midmid) return false; //注意这里要特判 
        int expense=sum[i]-sum[last];
//      D(expense);E;
        if(expense>mid){
            ans++;last=i-1;
        }
    }
//  D(ans);E;
    return ans<=m;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Monthly Expense.in","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i],sum[i]=sum[i-1]+a[i];
        maxa=max(maxa,a[i]);
    }
//  check(100);
    int l=0,r=INF;
    while(l>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    cout<

POJ 3104: Drying
不开long long会WA(原因未知),用了cin会TLE。(这两个坑坑了了我五六次提交)
剩下就很简单了,二分时间然后check吹风机使用的总时间是否<=mid即可。
具体地说,对于二分值mid,如果衣服i的含水量<=mid,则不用操作。
如果衣服i的含水量>mid,则设吹风机对衣服i吹了x秒。
则x满足不等式kx+(mid-x)>=a[i],即x>=(a[i]-mid)/(k-1)。
也就是说,对于衣服i,需要吹ceil((a[i]-mid)/(k-1))秒。
ps:注意这里k-1可能为0,所以要特判k=1的情况。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
不开long long会WA(原因未知),用了cin会TLE。(这两个坑坑了了我五六次提交) 
剩下就很简单了,二分时间然后check吹风机使用的总时间是否<=mid即可。
具体地说,对于二分值mid,如果衣服i的含水量<=mid,则不用操作。
如果衣服i的含水量>mid,则设吹风机对衣服i吹了x秒。
则x满足不等式kx+(mid-x)>=a[i],即x>=(a[i]-mid)/(k-1)。 
也就是说,对于衣服i,需要吹ceil((a[i]-mid)/(k-1))秒。 
ps:注意这里k-1可能为0,所以要特判k=1的情况。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100000+5;
const int INF=0x3f3f3f3f;
ll n,a[maxn],k,maxa=0;
bool check(int mid){
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(a[i]<=mid) continue;
        if((a[i]-mid)%(k-1)==0) ans+=(a[i]-mid)/(k-1);
        else ans+=(a[i]-mid)/(k-1)+1;
    }
    return ans<=mid;
}
int main() {
//  ios::sync_with_stdio(false);
    //freopen("Drying.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]),maxa=max(maxa,a[i]);
    cin>>k;
    if(k==1){
        cout<>1;
        if(check(mid)) r=mid;
        else l=mid+1; 
    }
    cout<

POJ 3045: Cow Acrobats
题解链接 https://www.cnblogs.com/wmrv587/p/3700452.html
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
https://www.cnblogs.com/wmrv587/p/3700452.html
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=50000+5;
const int INF=0x3f3f3f3f;
struct node{
    int w,s;
    bool operator<(const node& h)const{return w+s>n;
    for(int i=1;i<=n;i++) cin>>a[i].w>>a[i].s;
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        ans=max(ans,W-a[i].s);
        W+=a[i].w;
//      D(W);
    }
    cout<

01分数规划

POJ 2976: Dropping tests
设答案为mid,二分答案。
具体check过程如下:
,则
即,只要对排序后,取较大的n-k个,判断和是否大于等于0即可。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
设答案为mid,二分答案。
具体check过程如下:
∑ai/∑bi>=mid,则∑ai-mid*∑bi>=0
即∑(ai-mid*bi)>=0,只要对ai-mid*bi排序后,取较大的n-k个,判断和是否大于等于0即可。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int INF=0x3f3f3f3f;
const double eps=1e-5;
int n,k;
double a[maxn],b[maxn],c[maxn];
bool check(double mid){
    for(int i=1;i<=n;i++) c[i]=a[i]-mid*b[i];
    sort(c+1,c+n+1);
    double sum=0;
    for(int i=k+1;i<=n;i++) sum+=c[i];
    return sum>=0;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Dropping tests.in","r",stdin);
    while(cin>>n>>k){
        if(n==0&&k==0) break;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>b[i];
        double l=0,r=1;//ai<=bi
        while(r-l>eps){
            double mid=(r+l)/2;
            if(check(mid)) l=mid;
            else r=mid;
        }
        cout<<(int)((l+0.005)*100)<

POJ 3111: K Best
方法同Dropping tests。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
方法同Dropping tests。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100000+5;
const int INF=0x3f3f3f3f;
const double eps=1e-5;
int n,k;
struct node{
    double v,w,c;
    int id;
    bool operator<(const node& h)const{return c>h.c;}
}a[maxn],b[maxn];
bool check(double mid) {
    for(int i=1; i<=n; i++) b[i].c=a[i].v-mid*a[i].w,b[i].id=a[i].id;
    sort(b+1,b+n+1);
    double sum=0;
    for(int i=1; i<=k; i++){
        sum+=b[i].c;
    }
    return sum>=0;
}
int main() {
//  ios::sync_with_stdio(false);
    //freopen("K Best.in","r",stdin);
    cin>>n>>k;
    for(int i=1; i<=n; i++) scanf("%lf%lf",&a[i].v,&a[i].w),a[i].id=i;
    double l=0,r=INF;
    while(r-l>eps) {
        double mid=(r+l)/2;
//      D(l);D(r);D(mid);E;
        if(check(mid)) l=mid;
        else r=mid;
    }
    for(int i=1;i<=k;i++) printf("%d ",b[i].id);
    return 0;
}
#endif
#ifdef method_2
/*

*/

#endif
#ifdef method_3
/*

*/

#endif

第k大值

POJ 3579: Median
二分答案,然后用upper_bound来求绝对值差在mid以内的数对的个数即可。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
二分答案,然后用upper_bound来求绝对值差在mid以内的数对的个数即可。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100000+5;
const int INF=0x3f3f3f3f;
int n,a[maxn],ans,l,r;
ll m;
ll check(int mid){ //返回绝对值差在mid以内的数对的个数 
    ll sum=0;
    for(int i=1;i<=n;i++){
        sum+=(ll)(upper_bound(a+i+1,a+n+1,a[i]+mid)-(a+i+1));
//      D(l);D(r);D(i);D(mid);D(sum);E;
    }
    return sum;
}
int main() {
//  ios::sync_with_stdio(false);
    //freopen("Median.in","r",stdin);
    while(cin>>n){
        m=(ll)n*(n-1)/2;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]); //cin会TLE 
        sort(a+1,a+n+1);
        l=0,r=INF;
        while(l>1;
            if(check(mid)>=(m+1)/2) r=mid;
            else l=mid+1;
            /*
            //不能这样写 反例
            4
            2 2 4 5 
            int mid=l+r+1>>1;
            if(check(mid)<=(m+1)/2) l=mid;
            else r=mid-1;
            */
        }
        cout<

POJ 3685: Matrix
观察这个式子,可以发现j不变的情况下,随着i的增大,Aij也相应增大。
由这个受到启发:二分枚举第M小的数,然后按列寻找,找到第一个大于这个数的位置,就可以知道该列中有多少个数是大于这个数。
难点是二分范围和精度问题,因为用int结果WA无数次,最后一个个换掉,最后全部变成long long才过。
代码如下

/*

*/
#define method_2
#ifdef method_1
/*
打表用 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100+5;
const int INF=0x3f3f3f3f;
int n=15,a[maxn][maxn];
int main() {
    ios::sync_with_stdio(false);
    freopen("Matrix.in","r",stdin);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=i*i+100000*i+j*j-100000*j+i*j;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100+5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
ll n,T;
ll m;
ll cal(ll i,ll j){
    return i*i+100000*i+j*j-100000*j+i*j;
}
/*
ll check(ll now){ //求矩阵中有多少数字小于等于mid 
    ll sum=0;
    for(int i=1;i<=n;i++){ //枚举列 
        int l=0,r=n+1;
        while(l>1;
            if(cal(mid,i)>=now) r=mid;
            else l=mid+1;
        }
        sum+=r;
    }
    return sum;
}
*/
ll check(ll now){ //求矩阵中有多少数字小于mid 
    ll sum=0;
    for(ll i=1;i<=n;i++){ //枚举列 
        ll l=1,r=n+1;
        while(l>1;
            if(cal(mid,i)>now) r=mid;
            else l=mid+1;
        }
        sum+=l-1;
    }
    return sum;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Matrix.in","r",stdin);
    cin>>T;
    while(T--){
        cin>>n>>m;
//      ll l=-100000ll*n,r=n*n+100000ll*n+n*n+n*n; //用这个紧的范围 耗时为[-INF,INF]的一半 
        ll l=-INF,r=INF; 
        while(l>1;
            if(check(mid)>=m) r=mid;
            else l=mid+1;
            */
            ll mid=l+r>>1;
            if(check(mid)

最小化第k大值

POJ 2010: Moo University - Financial Aid
题解链接 https://www.jianshu.com/p/5765513ce5b4
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
将所有奶牛按照score升序排序后,从大向小一次尝试作为中位数是否可行即可。
为了提高对于每头奶牛判断的效率,用sum1[i]表示i左侧c/2头牛的最小aid和,可用一个大小固定为n/2的大根堆来求,每次与堆顶元素比较。
同理可以对称的定义sum2[i]。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=100000+5;
const int INF=0x3f3f3f3f;
int n,c,f,sum1[maxn],sum2[maxn];//sum1[i]表示i左侧c/2头牛的最小aid和 用一个大小固定为n/2的大根堆来求 每次与堆顶元素比较 
struct node{
    int s,aid;
    bool operator<(const node& h)const{return sq;
void pre1(){ 
    while(q.size()) q.pop();
    int sum=0,nowf; //堆中所有元素和 
    for(int i=1;i<=c;i++){
        sum1[i]=sum;
        if(q.size()) nowf=q.top();
        if(q.size()=1;i--){
        sum2[i]=sum;
        if(q.size()) nowf=q.top();
        if(q.size()=n/2+1;i--){ //从大到小依次枚举当前值能否作为中位数 
        if(cow[i].aid+sum1[i]+sum2[i]<=f){
            flag=true;
            cout<

POJ 3662: Telephone Lines
二分答案,设当前答案为mid。
将所有边的边权-mid后,权值<=0的边边权设为0,否则为1。
然后,从1到n跑最短路,如果最短路<=k,则满足条件。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
二分答案,设当前答案为mid。
将所有边的边权-mid后,权值<=0的边边权设为0,否则为1。
然后,从1到n跑最短路,如果最短路<=k,则满足条件。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int maxp=10000+5;
const int INF=0x3f3f3f3f;
int n,p,k,head[maxn],tot=1,vis[maxn];
dequeq;
struct node{
    int from,to,c;
}edge[maxp<<1],edge1[maxp<<1];
void add(int from,int to,int c){
    edge[++tot].from=head[from],edge[tot].to=to,edge[tot].c=c;
    edge1[tot].from=head[from],edge1[tot].to=to;
    head[from]=tot;
}
void init(){
    while(q.size()) q.pop_front();
    q.push_back(make_pair(1,0));
    memset(vis,0,sizeof(vis));
}
int bfs(){
    init();
    while(q.size()){
        pii now=q.front();q.pop_front();
        int x=now.first,d=now.second;
        if(x==n) return d;
        if(vis[x]) continue;
        vis[x]=1;
        for(int i=head[x];i;i=edge1[i].from){
            int y=edge1[i].to,c=edge1[i].c;
            if(c==0) q.push_front(make_pair(y,d));
            else q.push_back(make_pair(y,d+1));
        }
    }
    return INF;
}
bool check(int mid){
    for(int i=2;i<=2*p+1;i++){
        edge1[i].c=edge[i].c-mid;
        if(edge1[i].c<=0) edge1[i].c=0;
        else edge1[i].c=1;
    }
    return bfs()<=k;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Telephone Lines.in","r",stdin);
    cin>>n>>p>>k;
    int from,to,c;
    for(int i=1;i<=p;i++) cin>>from>>to>>c,add(from,to,c),add(to,from,c);
//  D(tot);
    int l=0,r=INF;
    while(l>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    r==INF?cout<<-1:cout<

其他二分搜索

POJ 1759: Garland

因此可以根据和递推。
又因为递推式关于单调递增,所以越小,答案就越小,所以二分即可。
注意天坑:n=3时,答案是-0.00,要纠正为0.00。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
h_i=2*h_i-1-h_i-2+2
因此可以根据h1和h2递推。
又因为递推式关于hi-1单调递增,所以h_2越小,答案就越小,所以二分h2即可。 
注意天坑:n=3时,答案是-0.00,要纠正为0.00。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=1000+5;
const int INF=0x3f3f3f3f;
const double eps=1e-5;
int n;
double A;
double cal(double mid){ //计算B 
    double pre1=A,pre2=mid,temp;
    for(int i=3;i<=n;i++){
        temp=2*pre2-pre1+2;
        pre1=pre2;pre2=temp;
    }
    return temp;
}
bool check(double mid){
    double pre1=A,pre2=mid,temp;
    for(int i=3;i<=n;i++){
        temp=2*pre2-pre1+2;
        if(temp<0) return false;
        pre1=pre2;pre2=temp;
    }
    return true;
}
int main() {
//  ios::sync_with_stdio(false);
    //freopen("Garland.in","r",stdin);
    cin>>n>>A;
    double l=0.0,r=(double)INF;
    while(r-l>eps){
        double mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
    double ans=cal(l);
    if(ans

POJ 3484: Showstopper
原理同防线。
由于只有一个点有奇数个数,则如果某个点的前缀和为奇数,
这个位置一定在左边,否则在右边,二分位置即可。
注意输入很恶心。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
原理同防线。
由于只有一个点有奇数个数,则如果某个点的前缀和为奇数,
这个位置一定在左边,否则在右边,二分位置即可。 
注意输入很恶心。 
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define D(x) cout<<#x<<" = "<pii;
const int maxn=20000+5; //poj上有人手测出的二分范围 
const int maxp=100+5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
char a[maxn];
int n;
vectorx,y,z;
bool read(){
    bool flag=false;
    x.clear();y.clear();z.clear();
    while(gets(a)){
        if(strlen(a)==0){
            if(flag==true) break;
            else continue;
        }
        ll xx,yy,zz;
        sscanf(a,"%lld%lld%lld",&xx,&yy,&zz);
        flag=true;
        x.push_back(xx);y.push_back(yy);z.push_back(zz);
    }
    n=x.size();
    return flag;
}
ll check(ll mid){ //求1~mid的前缀和 
    ll ans=0;
    for(int i=0;imid) continue;
        ans+=(min(y[i],mid)-x[i])/z[i]+1;
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Showstopper.in","r",stdin);
    while(read()){
        ll l=0,r=INF;
        while(l>1;
            if(check(mid)&1) r=mid;
            else l=mid+1;
        }
        if(r==INF) cout<<"no corruption"<=x[i]){
                    if((r-x[i])%z[i]==0) tot++;
                }
            }
            cout<

你可能感兴趣的:(二分搜索)