二分查找

四个数列

题目要求

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
Input
第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
Output
输出不同组合的个数。

求解思路

如果直接暴力求解,算法复杂度将达到n4。而先计算出A、B队列的n2种组合,再利用二分查找的方式对于C、D队列的每种组合计算出A、B队列中符合要求的组合数目x,最后把所有x求和即为组合个数。这样复杂度为n2logn2

在计算出A、B队列中符合要求的组合数目x时,使用了二分查找的方法,查找出队列中最后一个符合要求的元素的位置和第一个符合要求的位置,二者相减加一即为x

代码
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;

int a[4001];
int b[4001];
int c[4001];
int d[4001];
int ab[16000001];
int n;
int find(int x)
{
	int difference = 0;
	int l = 0,  r = n*n-1, ans = -1;
	while (l <= r)
	{
		int middle = (l + r) >> 1;
		if (ab[middle] == x)
		{
			ans = middle;
			l = middle + 1;
		//	cout << "a";
		}
		else if (ab[middle] > x)  r = middle - 1;
		else l = middle + 1;
	}
//	cout << ans;
	if (ans == -1)//x最后一个位置
	{
		return 0;
	}
	
	difference = ans;
	l = 0; r = n*n-1; ans = -1;
	while (l <= r)
	{
		int middle = (l + r) >> 1;
		if (ab[middle] == x)
		{
			ans = middle;
			r = middle - 1;
		}
		else if (ab[middle] > x)  r = middle - 1;
		else l = middle + 1;
	}//x最前的一个位置
	difference = difference - ans + 1;
	return difference;
}
int main()
{

	cin >> n;
	for (int i = 0; i < n; i++)
	{
//		scanf("%d%d%d%d", &a[i], &b[i], &c[i], &d[i]);
		cin >> a[i] >> b[i] >> c[i] >> d[i];
	}
	for(int j=0;j<n;j++)
		for (int k = 0; k < n; k++)
			ab[j * n + k] = a[j] + b[k];

	sort(ab, ab + n * n);
	int sum = 0;
	for (int j = 0; j < n; j++)
	{
		for (int k = 0; k < n; k++)
		{
			int cd = c[j] + d[k]; 
			cd = cd * (-1);
//			cout << cd << endl;
			sum += find(cd); 
		}
	}
//	for (int j = 0; j < n; j++)
//		for (int k = 0; k < n; k++)
//			cout << ab[j * n + k] << endl;
	printf("%d", sum);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值