Week4--作业3--B- 四个数列

题目描述

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!

输入

   输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。 每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。 然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。

输出

  输出不同组合的个数。

样例输入

在这里插入图片描述

样例输出

在这里插入图片描述

思路

综述

这道题是一道整数二分算法题;
题干要求:a+b+c+d=0
如果四层循环O(n^4)在数据量是4000的情况下太高,不合适;
变换:a+b = -c-d;
只需要两遍O(n^2)复杂度循环解决

过程

step1:输入数据
注意:scanf会缩短时间

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

step2:
计算-c-d并排序,以便用于二分;

	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			
			num2[jishi] = -c[i] - d[j];
			jishi++;
		}
	}	
	sort(num2, num2 + (n * n),cmp);

step3:
计数:
每计算一个a+b 便二分搜索,找到排好序的数组中第一个等于a+b的序号和最后一个a+b的序号,两者相减+1便是结果;
循环上述,并且累加;

	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			num1[jishi] = a[i] + b[j];
			seq = findmax(num1[jishi]);
			
			if (seq != -1)
				tot += seq - findmin(num1[jishi]) + 1;
			jishi++;
		}
	}

总结

共发现4个问题

问题1:(用于记录最大结果数的tot)

这个题,用于存储结果的值,更合理的是用长整型,long long,虽然int也可以过,但是如果出现6个数组都是4000个0那么就会出错

问题2:find函数的问题

在下代码处,加注释“记录”的地方,需要特别注意,应该是length-1;(当然其他写法如while(l<r)可有其他处理方式)

int findmin(long long x){
	int l=0;
	int r = n * n-1;	//记录!!!! 
	int ans = -1;
	int mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (num2[mid] == x) {
			ans = mid;
			r = mid - 1;
		}
		else if (num2[mid] < x) l = mid + 1;
		else if (num2[mid] > x) r = mid - 1;
	}
	return ans;
}

问题3:scanf和cin问题

左侧是使用cin的耗时,右侧是使用scanf的耗时
在这里插入图片描述

问题4:计数器的问题

在如下的代码中,jishi这个int型变量,可以用"n*i+j"来代替,但是外层双循环,复杂度高,用jishi来代替可以一定程度优化。

	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			num1[jishi] = a[i] + b[j];
			seq = findmax(num1[jishi]);
			
			if (seq != -1)
				tot += seq - findmin(num1[jishi]) + 1;
			jishi++;
		}
	}

代码

//cmp超时问题
//find函数问题 
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;
long long n;
int tot = 0;


long long a[5000], b[5000], c[5000], d[5000];
long long num1[16000050], num2[16000050];
bool cmp(long long &a,long long  &b) {
	return a < b;//小到大
}
//小到大
int findmin(long long x){
	int l=0;
	long long r = n * n-1;	//记录!!!! 
	int ans = -1;
	int mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (num2[mid] == x) {
			ans = mid;
			r = mid - 1;
		}
		else if (num2[mid] < x) l = mid + 1;
		else if (num2[mid] > x) r = mid - 1;
	}
	return ans;
}
int findmax(long long x) {
	int l=0;
	long long r = n * n-1;
	int ans = -1;
	int mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (num2[mid] == x) {
			ans = mid;
			l = mid + 1;
		}
		else if (num2[mid] < x) l = mid + 1;
		else if (num2[mid] > x) r = mid - 1;
	}
	return ans;
}
int main() {
	long long i,j;
	cin >> n;

	for (i = 0; i < n; i++) {
		//cin >> a[i] >> b[i]//cmp超时问题
//find函数问题 
#include <iostream>
#include <queue>
#include <algorithm>

using namespace std;
long long n;
int tot = 0;


int a[5000], b[5000], c[5000], d[5000];
int num1[16000050], num2[16000050];
bool cmp(long long &a,long long  &b) {
	return a < b;//小到大
}
//小到大
int findmin(long long x){
	int l=0;
	int r = n * n-1;	//记录!!!! 
	int ans = -1;
	int mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (num2[mid] == x) {
			ans = mid;
			r = mid - 1;
		}
		else if (num2[mid] < x) l = mid + 1;
		else if (num2[mid] > x) r = mid - 1;
	}
	return ans;
}
int findmax(long long x) {
	int l=0;
	long long r = n * n-1;
	int ans = -1;
	int mid;
	while (l <= r) {
		mid = (l + r) >> 1;
		if (num2[mid] == x) {
			ans = mid;
			l = mid + 1;
		}
		else if (num2[mid] < x) l = mid + 1;
		else if (num2[mid] > x) r = mid - 1;
	}
	return ans;
}
int main() {
	long long i,j;
	cin >> n;

	for (i = 0; i < n; i++) {
		//cin >> a[i] >> b[i] >> c[i] >> d[i];
		scanf("%lld %lld %lld %lld", &a[i], &b[i], &c[i], &d[i]);

	}
	
int jishi=0;
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			
			num2[jishi] = -c[i] - d[j];
			jishi++;
		}
	}	
	sort(num2, num2 + (n * n),cmp);
	
int seq;
jishi=0;
	for (i = 0; i < n; i++) {
		for (j = 0; j < n; j++) {
			num1[jishi] = a[i] + b[j];
			seq = findmax(num1[jishi]);
			
			if (seq != -1)
				tot += seq - findmin(num1[jishi]) + 1;
			jishi++;
		}
	}

	cout << tot << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值