关于二分操作的基本应用

前言:二分答案最重要的一点就是答案具有连续性,即有单调性的连续函数。

一:可以验证答案是否正确,来改变答案区间

如:求零点,求最接近元素。

还可以用于某些去掉重复元素的操作。

这一类比较简单,不做详细解释

二:最大化最小值/最小化最大值

如noip2015:

2257: [NOIP2015]跳石头

Description

一年一度的“跳石头”比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M块岩石(不能移走起点和终点的岩石)。

Input

输入第一行包含三个整数L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。  接下来N行,每行一个整数,第i行的整数Di(0 < Di < L)表示第i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

Output

输出只包含一个整数,即最短跳跃距离的最大值。

Sample Input

25 5 2
2
11
14
17
21

Sample Output

4

HINT

样例说明】  将与起点距离为2和14的两个岩石移走后,最短的跳跃距离为4(从与起点距离17的岩石跳到距离21的岩石,或者从距离21的岩石跳到终点)。

【数据规模与约定】  对于20%的数据,0 ≤ M ≤ N ≤ 10。  对于50%的数据,0 ≤ M ≤ N ≤ 100。  对于100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

下面代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int lenth[50001];
int len,n,m;
bool check(int step)
{
    int count=0,temp=0,i;
    for(i=1;i<=n;i++)
    {
        if(lenth[i]-lenth[temp]>=step)
        {
            temp=i;
        }
        else count++;
    }
    if(count>m)
    return false;
    if(len-lenth[temp]<step)
    return false;
    return true;
}
int main()
{
    int i,j;
    scanf("%d%d%d",&len,&n,&m);
    int l=0,r,mid;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&lenth[i]);
    }
    r=len;
    while(l+1<r)
    {
        mid=(l+r)/2;
        if(check(mid))
        l=mid;
        else r=mid;
    }
    if(check(r))
    cout<<r;
    else
    cout<<l;

}

三:关于二分验证的方法

1.按照题意模拟验证

比较简单不详细说明

2.dp验证

如:

魔棒

时间限制: 1 Sec  内存限制: 128 MB 提交: 50  解决: 8 [提交][状态]

题目描述

有一个英雄,初始生命值是hp(生命值无上限),在接下来的n秒内,每秒会受到一次伤害,第i秒受到的伤害值为a[i]。这个英雄有一个道具“魔杖”,魔杖的初始能量为0,每受到一次伤害,积攒一点能量。在英雄受到伤害后,可以立即释放魔棒中的能量,恢复15*[能量点数]的生命值,且魔棒的点数清零。释放能量有施法间隔cd(cd是正整数),即相邻的两次释放的时间间隔至少有cd秒。任何时刻当hp<=0时视为死亡,问这个英雄存活下来的前提下,cd的值最大可以是多少?

注意,若a[i]为负,受到“伤害”后实际上生命值是增加的,魔棒仍然积攒能量。

输入

第一行两个正整数n,hp,含义如题目所述。

第二行n个整数,分别是a[1]..a[n]。

输出

一个数,最大的cd,cd是一个正整数。

如果cd没有上限,输出“No upper bound.”;如果无论如何都不能存活,输出-1。

样例输入

7 30
20 5 30 4 10 5 20

样例输出

2

提示

对于30%的数据,n<=12;

对于100%的数据,n<=500,|a[i]|<=1000;

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll a[550],hp,dp[501][501][2],sp[501][2],n;
bool check(int mid)
{
    memset(dp,0,sizeof(dp));
    memset(sp,0,sizeof(sp));
    dp[0][0][0]=hp;
    sp[0][0]=hp;
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=2;j<=i;j++)
        {
            dp[i][j][0]=dp[i-1][j-1][0]-a[i];
            dp[i][j][1]=dp[i-1][j-1][1]-a[i];
            if(dp[i][j][0]>0)
            {
                sp[i][0]=max(sp[i][0],dp[i][j][0]+j*15);
            }
            if(dp[i][j][1]>0&&j>=mid)
            {
                sp[i][1]=max(sp[i][1],dp[i][j][1]+j*15);
            }
        }
        dp[i][1][0]=dp[i-1][0][0]-a[i];
        if(dp[i][1][0]>0)
        {
            sp[i][0]=max(sp[i][0],dp[i][1][0]+15);
        }
        int step=max(sp[i-1][0],sp[i-1][1]);
        if(step>=a[i])
        {
            dp[i][0][1]=step+15-a[i];
        }
        dp[i][1][1]=step-a[i];
    }
    for(i=0;i<=n;i++)
    {
        if(dp[n][i][0]>0||dp[n][i][1]>0)
        return 1;
    }
    return 0;
}
int main()
{
    ll i,j;
    ll count=0;
    int step=0;
    scanf("%lld%lld",&n,&hp);
    ll ppp=hp;
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    hp=ppp;
    for(i=1;i<=n;i++)
    {
        hp=hp-a[i];
        count++;
        if(hp<=0&&step==0)
        {
            hp=hp+(count*15);
            step=1;
        }
        if(hp>0&&i==n)
        {
            cout<<"No upper bound.";
            return 0;
        }
    }
    hp=ppp;
    ll l=0,r=n,mid;
    while(l+1<r)
    {
        mid=(l+r)/2;
        if(check(mid)==1)
        {
            l=mid;
        }
        if(check(mid)==0)
        {
            r=mid;
        }
    }
    if(l==0)
    cout<<"-1";
    else
    cout<<l;
}

dp验证比较常见,不再概述

3.最短路验证

问题 A: 收费站(cost.pas/c/cpp)

题目描述

在某个遥远的国家里,有n个城市。编号为1,2,3,……,n。

这个国家的政府修建了m条双向的公路。每条公路连接着两个城市。沿着某条公路,开车从一个城市到另一个城市,需要花费一定的汽油。

开车每经过一个城市,都会被收取一定的费用(包括起点和终点城市)。所有的收费站都在城市中,在城市间的公路上没有任何的收费站。

小红现在要开车从城市u到城市v(1<=u,v<=n)。她的车最多可以装下s升的汽油。在出发的时候,车的油箱是满的,并且她在路上不想加油。

在路上,每经过一个城市,她要交一定的费用。如果她某次交的费用比较多,她的心情就会变得很糟。所以她想知道,在她能到达目的地的前提下,她交的费用中最多的一次最少是多少。这个问题对于她来说太难了,于是她找到了聪明的你,你能帮帮她吗?

输入

第一行5个正整数,n,m,u,v,s。分别表示有n个城市,m条公路,从城市u到城市v,车的油箱的容量为s升。

接下来有n行,每行1个正整数,fi。表示经过城市i,需要交费fi元。

再接下来有m行,每行3个正整数,ai,bi,ci(1<=ai,bi<=n)。表示城市ai和城市bi之间有一条公路,如果从城市ai到城市bi,或者从城市bi到城市ai,需要用ci升汽油。

输出

仅一个整数,表示小红交费最多的一次的最小值。

如果她无法到达城市v,输出-1。

样例输入

 4 4 2 3 8
 8
 5
 6
 10
 2 1 2
 2 4 1
 1 3 4
 3 4 3

样例输出

8

提示

对于60%的数据,满足n<=200,m<=10000,s<=200

对于100%的数据,满足n<=10000,m<=50000,s<=1000000000

对于100%的数据,满足ci<=1000000000,fi<=1000000000,可能有两条边连接着相同的城市。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 0X3F3F3F3F
using namespace std;

int n,m,u,v,s,f[10005],dist[10005];
vector<int>g[10005],w[10005];

struct data
{
    int d,id;
    friend bool operator < (data a,data b)
    {
        return a.d>b.d;
    }
};

void Dij(int s,int *d,int limit)
{
    priority_queue<data>pq;
    for(int i=1;i<=n;i++) d[i]=inf;
    pq.push((data){0,s});
    d[s]=0;

    while(!pq.empty())
    {
        data t=pq.top(); pq.pop();
        int i=t.id;
        if(f[i]>limit) continue;
        if(t.d>d[i]) continue;
        d[i]=t.d;

        for(int k=0;k<g[i].size();k++)
        {
            int j=g[i][k],c=w[i][k];
            if(f[j]>limit) continue;
            if(d[i]+c<d[j])
            {
                d[j]=d[i]+c;
                pq.push((data){d[j],j});
            }
        }
    }
}

int main()
{
    scanf("%d%d%d%d%d",&n,&m,&u,&v,&s);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&f[i]);
    }

    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        g[x].push_back(y);
        g[y].push_back(x);
        w[x].push_back(z);
        w[y].push_back(z);
    }

    int A=0,B=1000000000,ans;
    while(A<=B)
    {
        int mid=(A+B)/2;
        Dij(u,dist,mid);
        if(dist[v]<=s)
        {
            B=mid-1;
            ans=mid;
        }
        else
        {
            A=mid+1;
        }
    }
    if(B==1000000000)
    {
        printf("-1\n");
    }
    else printf("%d\n",ans);
    return 0;
}

验证的方法还有很多,目前遇到的只有这几种,遇到后会再做补充。

时间: 2024-08-27 19:37:38

关于二分操作的基本应用的相关文章

二分算法~~~大综合

二分:一个非常神奇的算法:永远记住二分,分的是答案,直接在答案在的区间范围中二分,分出一个值,就判断是不是           答案,并进行转移 二分答案: 如果已知候选答案的范围[min,max],有时候我们不必通过计算得到答案,只需在此范围内应用“二分”的过程,逐渐靠近答案(最后,得到答案)! 一.何时可以使用“二分答案” 不是任何题目都适合使用“二分答案”的,我Sam观察到一般有以下的一些特征: A. 候选答案必须是离散的 ,且已知答案的范围是:[最小值min, 最大值max] (连续区间

BZOJ 2527 Poi2011 Meteors 整体二分+线段树 / 可持久化线段树(MLE)

题目大意:给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值 首先我们考虑暴力想法 对于每个国家分开讨论 二分操作次数 但是这样每次Judge的时候我们要模拟1~mid所有的操作 浪费在这里的复杂度实在太大 这样做每个国家需要模拟O(klogk)次操作 时间复杂度O(nklogk) TLE 我们需要对浪费在这里的复杂度做一些改进 1.可持久化线段树(MLE) 每次二分一个mid之后 我们要找到mid次

数据结构与算法实践 之 二分查找初识

今天起,我要对数据结构和基本的算法进行一些简单的复习,并在复习的基础上对其进行深入的挖掘.这篇文章先对二分查找进行一个简要的复习,在之后的文章中会对其进行深入的学习. 二分查找又叫折半查找,是最基本的几种查找算法之一.简单的看,二分法查找主要应用于在一个有序数列中进行元素的查找,其基本思路是,先用我们要查找的元素与这个有序数列中的中间位置的元素进行比较(在此我们姑且称这个元素为"中间位置元素"吧,至于这个元素怎么求我在后面会详细说明),如果相等,则返回这个"中间位置元素&qu

Luogu P3527 [POI2011]MET-Meteors 整体二分

思路:整体二分 提交:4次 错因:树状数组开的$int$ 题解: 二分操作序列,将仅用$[l,md]$即可满足要求的国家递归到左半边,将仅用$[l,md]$不能满足要求的国家,把他们的要求去掉左半边的贡献,递归到右半边. 具体来说,开一个以空间站为下标的树状数组(把环展成链),区间加单点求和转化为差分和前缀和,依次加入$[l,md]$中的所有操作区间: 然后每个国家枚举自己的所有空间站,计算贡献,判断前$[l,md]$是否满足,来决定向左右递归的方向. #include<iostream> #

OpenCV Tutorials &mdash;&mdash; Basic Thresholding Operations

  Threshold Binary This thresholding operation can be expressed as: So, if the intensity of the pixel is higher than , then the new pixel intensity is set to a . Otherwise, the pixels are set to . 二分 ~~ 阈值化最朴素的形式   Threshold Binary, Inverted This thr

World Finals 2017

Need for Speed Sheila is a student and she drives a typical student car: it is old, slow, rusty, and falling apart. Recently, the needle on the speedometer fell off. She glued it back on, but she might have placed it at the wrong angle. Thus, when th

3720: Gty的妹子树

3720: Gty的妹子树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1440  Solved: 482[Submit][Status][Discuss] Description 我曾在弦歌之中听过你, 檀板声碎,半出折子戏. 舞榭歌台被风吹去, 岁月深处尚有余音一缕…… Gty神(xian)犇(chong)从来不缺妹子…… 他来到了一棵妹子树下,发现每个妹子有一个美丽度…… 由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣. 他想知道

LeetCode里有特色的问题存档

002* Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). 1 class Solution: 2 def findKth(self, A, A_start, B,

【分块】bzoj1901 Zju2112 Dynamic Rankings

区间k大,分块大法好,每个区间内存储一个有序表. 二分答案,统计在区间内小于二分到的答案的值的个数,在每个整块内二分.零散的暴力即可. 还是说∵有二分操作,∴每个块的大小定为sqrt(n*log2(n))比较快呢. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cmath> 5 using namespace std; 6 int n,a[10001],num[