题意:有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; } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-08-08 01:27:49