二分二分(POJ 2785 + HDU 2289)

手写二分:
若x在数组a[N]中,查找x在数组中的位置:

int find(int x)
{
    int mid,l=0,r=n-1;
    while(l<=r)
    {
         mid=(l+r)/2;
         if(a[mid]==x) return mid;
         else if(a[mid]>x) r=mid-1;
         else l=mid+1;
    }
    return -1; //如果没有找到,返回-1
}

若x不一定在数组中,查找数组中小于x的数有几个:(利用逐步逼近的方法)

int find(int x)
{
    int mid,l=0,r=n-1;
    while(r-l>1)
    {
        mid=(l+r)/2;
        if(a[mid]>=x) r=mid;
        else l=mid;
    }
    return l+1;
}

二分里面两个重要的函数:
lower_bound: 查找序列中的第一个出现的值大于等于x的位置
upper_bound: 返回的是第一个大于x的位置
使用方法都是: pos=lower_bound(a,a+n,x)-a;
pos=upper_bound(a,a+n,x)-a;
(并且在使用之前要把作用数组sort一次。)

比如:map中已经插入了1,2,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper_bound(2)的话,返回的就是3。


POJ 2785 – 4 Values whose Sum is 0
大概题意是有4列n行数,在每列中选择一个数,使得选出的4个数相加和为零。(n<=4000)。如果用枚举的方法,最大的情况大约O(4000*4000*4000*4000)。这样肯定是不行的。所以可以将前两列数和的情况全部枚举出来,后两列数的和的情况全部枚举出来分别放入两个数组里面(sumab[N]和sumcd[N])。然后将sumab[N]从0枚举到N,看是否能在sumcd[N]中找到与其对应的解,这里从sumcd[N]中找到对应的解的过程用到二分。

//头文件略
const int N = 4000+10;
int arr[N][4];
int sumab[N*N],sumcd[N*N];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++) for(int j=0;j<4;j++) scanf("%d",&arr[i][j]);
    int t=0;
    for(int i=0;i<n;i++) for(int j=0;j<n;j++) sumab[t++]=arr[i][0]+arr[j][1];
    int tt=0;
    for(int i=0;i<n;i++) for(int j=0;j<n;j++) sumcd[tt++]=arr[i][2]+arr[j][3];
    int sum=0;
    sort(sumcd,sumcd+tt);
    for(int i=0;i<t;i++)
    {
        int x=lower_bound(sumcd,sumcd+tt,0-sumab[i])-sumcd;
        for(int j=x;j<tt;j++)
        {
            if(sumcd[j]+sumab[i]>0) break;
            if(sumcd[j]+sumab[i]==0) sum++;
        }
    }
    printf("%d\n",sum);
    return 0;
}

HDU 2289–Cup
这题是一个圆台,底面半径是r,顶面半径是R,高为H,然后告诉现在装了v升水,问水的高度是多少?
这题也是二分,只不过要确保二分的精度问题。题目要求保留六位小数,那就设 EXP=1e-7;

#define pi acos(-1)
#define EXP 1e-7
using namespace std;
double r,R,H,V;
double cal_v(double x)
{
    return  pi*x*(r*r+pow(r+(R-r)/H*x,2)+r*(r+(R-r)/H*x))/3.0;
}
double _find(double L,double R)
{
    double mid;
    while(fabs(L-R)>EXP)
    {
        mid=(L+R)/2;
        if(cal_v(mid)>=V) R=mid;
        else L=mid;
    }
    return L;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lf %lf %lf %lf",&r,&R,&H,&V);
        printf("%.6lf\n",_find(0,H));
    }
    return 0;
}

(怎么感觉还是掌握不了手写二分…Orz…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值