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恰巧就是那个唯一的点。
下面是浮点二分的两种方法