!HDU 4334 集合各出一数和为0是否存在-思维、卡时间-(指针的妙用)

23 篇文章 0 订阅
12 篇文章 0 订阅

题意:有5个集合,集合的大小是n,每一个集合出一个数,问能不能找到五个数的和为0。数据范围:T<=50;n<=200

分析:

暴力枚举是n^5*T,超时,那么就要用一些技巧了。

这里有一个指针的妙用:如何在O(n)的复杂度找A,B,使得A+B==C(A,B分别属于一个数列a,b)。做法是先把a,b分别按升序排序,然后一个指针i指向a的首,指针j指向b的尾,判定指针指向的数的和是否==C,若等于则结束查找,若小于,则i++,若大于则 j- -,如果有一个指针已经走到了头还没找到A+B==C,则说明不存在这样的AB;

这个问题再变形:有三个数列a,b,c,如何在O(n^2)的复杂度找A,B,C,使得A+B==C。方法还是上面那个,就是多了一个遍历C的循环。

本题就是用了这个技巧,还用了一点分治的思想吧,其实不是分治,反正我就这样记了,就是说不要题目给的一个问题就想着把这个问题整体的解决,题目说求和,我们不是一定非要一步到位的求和啊,我们可以把求和这个问题分成:a1+a2;a3+a4;a5 求和就是(a1+a2)+(a3+a4)+a5,又因为是找等于0,所以就是:(a1+a2)+(a3+a4)==-a5,这样不就把问题转换成上面的模型了吗。

另外,排序+去重可以用set来做,set.insert()的时候会自动去重并且按升序排序。

代码:

#include<iostream>
#include<set>
#include<algorithm>
using namespace std;
set<long long> s1,s2;
int t,n;
long long a[6][300];
long long sum[3][50000];
int main()
{
	cin>>t;
	while(t--){
		cin>>n;
		s1.clear();
		s2.clear();
		for(int i=0;i<5;i++){
			for(int j=0;j<n;j++){
				cin>>a[i][j];
			}
		}
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				s1.insert(a[0][i]+a[1][j]);
				s2.insert(a[2][i]+a[3][j]);
			}			
		}
		for(int i=0;i<n;i++) a[4][i]*=(-1);
		int len1=0,len2=0;
		set<long long>::iterator it;
		for(it=s1.begin();it!=s1.end();it++) sum[1][len1++]=(*it);
		for(it=s2.begin();it!=s2.end();it++) sum[2][len2++]=(*it);
		int ok1=0;
		
		for(int i=0;i<n;i++){
			int ok=1;
			int j=0,k=len2-1;
			while(sum[1][j]+sum[2][k]!=a[4][i]){
				if(sum[1][j]+sum[2][k]<a[4][i]) j++;
				else k--;
				if(j>=len1||k<0){
					ok=0;break;
				}
			}
			if(ok){
				ok1=1;break;
			}
		}
		if(ok1) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值