week4-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:
输出不同组合的个数。

	输入输出样例

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
output:
5
样例输出解释:(-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

思路

  • 此题刚开始以为是一个暴力枚举,就是枚举A,B,C,D中的每一个元素然后进行相加即可,那么复杂度是O(n4),是受不了的。
  • 那么就进行少枚举一些点,是不是就可以了,将A和B进行一一枚举相加,C和D进行一一枚举相加,那么此时复杂度就是O(n2),是可以接受的,此时我们就可以,将A+B中的值和C+D中的值的相反数进行一一枚举相加,然后选择和为0的个数;
  • 将A+B中的值取反,然后去C+D中去找这个数的个数;那么就可以使用整数二分,先将C+D进行升序排序,然后通过整数二分先找到第一个出现的位置,然后再找到最后一个出现的位置,相减,就可以得到一个值对应的组合数;遍历即可

总结

  • 这种取数的问题,一眼看过去就是一个一个进行枚举,那么数据范围一大,特定超时,那么我们可以想办法进行使得枚举的点少一些,可以进行一些剪枝,排除一些情况的枚举;
  • 此题在做的时候,没有认真读题,应该是不去重的,因为一个数列中,相同的数是按不同数算的;教训(认真读题,别放过一个字,教训,不然就是0,而且该0)

代码

#include<iostream>
#include<algorithm>
using namespace std;	
int n;
int  last_location(int* p, int m)
{
	int l = 0, r = n*n - 1,ans = -1;
	while (l <=r)
	{
		int mid = (l + r) >> 1;
		if (p[mid] == m)
		{
			ans = mid;
			l = mid + 1;
		}
		else if (p[mid] > m) r = mid - 1;
		else
			l = mid + 1;
	}
	return ans;
}
int first_location(int* p, int m)
{
	int l = 0, r = n*n - 1, ans = -1;
	while (l <= r)
	{
		int mid = (l + r) >> 1;
		if (p[mid] == m)
		{
			ans = mid;
			r= mid - 1;
		}
		else if (p[mid] > m) r = mid - 1;
		else
			l = mid + 1;
	}
	return ans;
}
int main()
{
	cin >> n;
	int* a = new int[n];
	int* b = new int[n];
	int* c = new int[n];
	int* d = new int[n];
	int* e = new int[n * n];
	int* f = new int[n * n];
	for (int i = 0; i < n; i++)
			cin >> a[i] >> b[i] >> c[i] >> d[i];
	int k = 0;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
		{
		
			e[k] = a[i] + b[j];
			f[k] = c[i] + d[j];
			k++;
		}	
	sort(e, e + n * n);
	sort(f, f + n * n);
	int sum = 0;
	for (int i = 0; i < n * n; i++)
	{
		int m = first_location(f, -e[i]);
		int n = last_location(f, -e[i]);
		if(n!=-1) 
			sum = sum + n-m+1;
	}
	cout << sum << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值