POJ2785_4 Values whose Sum is 0_哈希||二分

4 Values whose Sum is 0
Time Limit: 15000MS Memory Limit: 228000K
Total Submissions: 21314 Accepted: 6410
Case Time Limit: 5000MS

Description

The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .

Input

The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 2 28 ) that belong respectively to A, B, C and D .

Output

For each input file, your program has to write the number quadruplets whose sum is zero.

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

Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

大致题意:

给出四列数,从每一列中选一个数,使这四个数之和等于零。求问一共有多少中选法。

大体思路:

1.二分。

1)计算前两列任意两个数之和(总共n*n个),并从小到大排序。

2)计算后两列任意两个数之和。对于每一个和,用二分法在前一个数组中搜索它的相反数。ans += 。

2.哈希。

1)计算前两列任意两书之和,用哈希表保存。

2)计算后两列任意两数之和,对每一个和,搜索哈希表中它的相反数的个数。ans+=。


哈希比二分快很多。。。


哈希代码:

#include<cstdio>
#include<cstring>

#define limit 4001
/*******创建哈希表时被模的那个数
		应是一个足够大的质数*******/
#define M 21331131
#define rep(i,a,b) for(int i=a; i<=b; i++)
#define mem(a,b) memset(a,b,sizeof(a))

const int inf = (1<<29);

struct node
{
	node(){}
	/*******三个参数:v 值, pre 同槽中的前一个元素, cnt 元素出现的次数*******/
	node(int v, int p, int c){val = v, pre = p, cnt = c;}
	int val, pre, cnt;
};

int a[limit], b[limit], c[limit], d[limit];//保存四列数
int n;
/*******head 是哈希表的头指针表, top是哈希表已填充段的最右端+1,即下一个填充位置*******/
int head [M], top;
/*******哈希表*******/
node hs [limit * limit];
/*******哈希表的插入函数*******/
void insert (int v)
{
	int pos = ( v + inf ) % M;//确定该元素所在的槽

	for(int i= head[pos]; i!= -1; i= hs[i].pre )//遍历该槽中的元素
		if(v == hs[i].val){//发现该值已存在于槽中
			hs[i].cnt++;//个数加1即可
			return;
		}

	hs[top] = node(v, head[pos], 1);//槽中还没有这个值,新建一个点,插入对应的槽中
									//v就是值,pre就是当前头指针表中该槽的值,次数初始化为1
	head[pos] = top ++;				//更新头指针表,该槽的第一个变成top。之后top自增,保持左闭右开
}
/********哈希表的查找函数*******/
int find (int v)
{
	int pos = ( v + inf ) % M;

	for(int i= head[pos]; i!= -1; i= hs[i].pre)
		if(v == hs[i].val)//找到该值,返回其出现的次数
			return hs[i].cnt;

	return 0;//没找到,返回0
}

int main ()
{
	while( ~scanf("%d",&n) ){
/********头指针表初始化为-1*******/
		mem(head,-1);

		rep(i,1,n)//输入四列数字
		scanf("%d%d%d%d", a+i, b+i, c+i, d+i);

		rep(i,1,n)//把前两列产生的和全部插入哈希表
		rep(j,1,n)
		insert(a[i]+b[j]);

		long long ans = 0;//答案初始化为0
		rep(i,1,n)
		rep(j,1,n){
			int t = c[i] + d[j];
			ans += find(-t);//查找t的相反数出现的次数
		}

		printf("%lld\n",ans);

	}

	return 0;

}


二分代码:

#include<cstdio>
#include<algorithm>
#define _max 4010

long long A [_max],B [_max],C[_max],D[_max];
long long E [_max*_max],F [_max*_max];
int n;
long long ans=0;

int upper (int x,int k)
{
	int l=0,r=k-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(E[mid]+x<=0) l=mid+1;
		else r=mid-1;
	}
	return x+E[l-1]==0?l-1:0;
}

int lower (int x,int k)
{
	int l=0,r=k-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(E[mid]+x>=0) r=mid-1;
		else l=mid+1;
	}
	return x+E[l]==0?l-1:0;
}

int main ()
{
	//freopen("in.txt","r",stdin);

	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%lld%lld%lld%lld",A+i,B+i,C+i,D+i);

	int i=0,k=0,j;
	for(;i<n;i++)
		for(j=0;j<n;j++)
			E[k]=A[i]+B[j],F[k]=C[i]+D[j],k++;
	std::sort(E,E+k);

	for(int i=0;i<k;i++)
		ans+=upper(F[i],k)-lower(F[i],k);

	printf("%I64d\n",ans);
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值