Week4:四个数列的和——二分

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

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

请你帮帮他吧!

输入格式
第一行:n(代表数列中数字的个数) (1≤n≤4000)

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

输出格式
输出不同组合的个数。

输入示例

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

输出示例

5

样例解释

符合题意的数字对: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

解题思路
显然直接遍历每个数列的话,复杂度会达到惊人的O(N^4)。龟龟,弗洛伊德算法看了都想哭。
那么这道题咋整呢?
答案是二分法。
对于A+B+C+D=0,我们可以进行适当的修改:
于是我们可以得到关系式:A+B=-(C+D)

那么我们只需要单独看A+B和C+D就可以了,复杂度一下子开了个根号呢!

由于二分需要严格的单调序列,所以我们输入完了之后,预处理一下数组A+B:让他们升序排列。

注意,这里的数组A+B指的是A的所有的数分别加上B的所有的数,如果总共有n个数组的话,那么A+B数组的大小应该是n^2。

然后,对于每个C+D,我们给他取个反,然后在A+B数组中二分查找和当前c+d的大小相同的值的“第一次出现的地方”和“最后一次出现的地方”。这是因为题目中说到了可能会有相同的数字,要当成不同的数来看待,即计数时算多次。

于是我们可以得到代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <algorithm>

using namespace std;

vector<int>ab;
vector<int>l[4];

int n;

bool LTOM(int a, int b)//升序排列函数
{
	return a < b;
}

int Front(int value)//找到第一个值
{
	int l = 0, r = n * n - 1, ans = -1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (ab[mid] == value)
		{
			ans = mid;
			r = mid - 1;
		}
		else if (ab[mid] > value) r = mid - 1;
		else
			l = mid + 1;
	}

	return ans;
}

int Last(int value)//找到最后一个值
{
	int l = 0, r = n * n - 1, ans = -1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (ab[mid] == value)
		{
			ans = mid;
			l = mid + 1;
		}
		else if (ab[mid] > value) r = mid - 1;
		else l = mid + 1;
	}

	return ans;
}

int main()
{
	
	cin >> n;

	int temp;
	for (int i = 0; i < n; i++)
	{		
		for (int j = 0; j <= 3; j++)
		{
			cin >> temp;
			l[j].push_back(temp);
		}
	}

	for(int i=0;i<n;i++)
		for (int j = 0; j < n; j++)
		{
			ab.push_back(l[0][i] + l[1][j]);
			//cd.push_back(-(l[2][i] + l[3][j]));
		}

	sort(ab.begin(), ab.end(), LTOM);//使ab升序

	int ans = 0;

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		{
			int cd = -(l[2][i] + l[3][j]);
			int f = Front(cd);
			if (f == -1) continue;
			else
				ans += (Last(cd) - f + 1);
		}

	cout << ans << endl;
}
发布了21 篇原创文章 · 获赞 3 · 访问量 415

猜你喜欢

转载自blog.csdn.net/qq_44506233/article/details/104979941
今日推荐