二分搜索总结(以program week4 work b为例)

B - 四个数列

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。

当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。

请你帮帮他吧!

Input
第一行:n(代表数列中数字的个数) (1≤n≤4000)

接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)

Output
输出不同组合的个数。

Sample Input
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45

Sample Output
5

Hint
样例解释: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

Answer

具体思路见注释

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int a[4010],b[4010],c[4010],d[4010];
int x[4010*4010],y[4010*4010];

int main()
{
	int n,ans=0;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i]>>b[i]>>c[i]>>d[i];
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			x[i*n+j]=a[i]+b[j];
			y[i*n+j]=c[i]+d[j];
		}
	}
	sort(x,x+n*n);
	for(int i=0;i<n*n;i++){
	    int l=0,r=n*n-1;
	    while(l<=r){
		    int mid=(l+r)>>1;
			if(x[mid]+y[i]>=0)r=mid-1;//一直向左找第一个符合条件的点,这里分开写也是一样的,但因为没有ans=mid这一步,其实就是一样的
			/*等效于
                  if(x[mid]+y[i]=0)r=mid-1;
                  else if(x[mid]+y[i]>0)r=mid-1;
            */
			else l=mid+1;
			while(x[l]==-y[i]&&l<n*n){//注意这里使用l而不是mid找,因为你要找到位于最左边那个符合条件的,然后依次往后找
				ans++;
				l++;
			}
	    }
	    
	}
	cout<<ans;
	return 0;
}

总结

在这里插入图片描述
在这里插入图片描述
p1中if里面的条件是>=(只有两个判断,即if和else),因为这里找的是>=,但其实如果找等于,也可以写成两个判断,不一定要像p2那样三个判断,因为这实际上是等效的(原因是比如p2的if里面也有l=mid+1,),而这里写l=mid+1还是r=mid-1,决定了是找x的第一个位置还是最后一个位置(前者为最后一个位置,后者为第一个位置)
比如例题里面(看注释)
可能会这样想,如果满足等于还r=mid-1,那此处符合条件的这个点不就被抛弃了?但其实不是的。试试就知道了(以例题为例)。假设这就是那个唯一符合条件的点,那么再来一遍while循环,ifelse就会得到l=mid+1,然后重新循环,因为不满足while条件,所以退出,此时的l恰巧就是那个唯一的点。

下面是浮点二分的两种方法
在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值