试题描述 |
给定4个n(1<=n<=4000)元素集合A,B,C,D,要求分别从中选取一个元素a,b,c,d,使得a+b+c+d=0。问:有多少种选法? 例如,A={-45,-41,-36,26,-32,100000},B={22,-27,53,30,-38,-54},C={42,56,-37,-75,-10,-6},D={-16,30,77,-46,62,45},则有5种选法:(-45,-27,42,30),(26,30,-10,-46),(-32,22,56,-46),(-32,30,-75,77),(-32,-54,56,30)。 |
输入 |
第一行包括一个整数 T,表示测试数据组数 |
输出 |
对于每组数据,一个整数表示一共有多少种选法,以空行结束一组数据 |
输入示例 |
1 |
输出示例 |
5 |
其他说明 |
试题来源:UVA1152 |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
讲解:
因为n<=4000,所以得从四重循环得优化,我们只需要将a,b,c,d排序去重,将a,b中所有组合存下来放入c,d的所有组合中二分查找。
代码实现:
#include<iostream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int xx=2010; int a[xx],b[xx],c[xx],d[xx],sum1[xx*xx],sum2[xx*xx],n,dex1,dex2,ans; bool ff(int l,int r,int s)//二分查找(略) { int i,j,mid; while(l<r) { mid=(l+r)/2; if(sum2[mid]==s) return true; else if(sum2[mid]>s) r=mid; else l=mid+1; } return false; } int main() { int i,j,t; scanf("%d",&t); while(t!=0) { ans=0; dex1=0; dex2=0; t--; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]); sort(a,a+n); //排序,便于去重 sort(b,b+n); sort(c,c+n); sort(d,d+n); for(i=0;i<n;i++) { if(i!=0 && a[i]==a[i-1]) continue; //去重 for(j=0;j<n;j++) { if(j!=0 && b[j]==b[j-1]) continue;//去重 sum1[dex1++]=a[i]+b[j];//将a+b的值存进sum里 } } sort(sum1,sum1+dex1);//sum排序 for(i=0;i<n;i++) { if(i!=0 && c[i]==c[i-1]) continue;//去重 for(j=0;j<n;j++) { if(j!=0 && d[j]==d[j-1]) continue;//去重 sum2[dex2++]=c[i]+d[j]; } } sort(sum2,sum2+dex2); for(i=0;i<dex1;i++) if(ff(0,dex2,-sum1[i])) ans++;//把 -c[i]-d[j]的值在sum里二分查找 cout<<ans<<endl; } return 0; }
如果内存超限(mle)我们可以牺牲时间:
#include<iostream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int xx=4010; int a[xx],b[xx],c[xx],d[xx],sum[xx*xx],n,dex,ans; bool ff(int l,int r,int s)//二分查找(略) { int i,j,mid; while(l<r) { mid=(l+r)/2; if(sum[mid]==s) return true; else if(sum[mid]>s) r=mid; else l=mid+1; } return false; } int main() { int i,j,t; scanf("%d",&t); while(t!=0) { ans=0; dex=0; t--; scanf("%d",&n); for(i=0;i<n;i++) scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]); sort(a,a+n); //排序,便于去重 sort(b,b+n); sort(c,c+n); sort(d,d+n); for(i=0;i<n;i++) { if(i!=0 && a[i]==a[i-1]) continue; //去重 for(j=0;j<n;j++) { if(j!=0 && b[j]==b[j-1]) continue;//去重 sum[dex++]=a[i]+b[j];//将a+b的值存进sum里 } } sort(sum,sum+dex);//sum排序 for(i=0;i<n;i++) { if(i!=0 && c[i]==c[i-1]) continue;//去重 for(j=0;j<n;j++) { if(j!=0 && d[j]==d[j-1]) continue;//去重 if(ff(0,dex,-c[i]-d[j])) ans++;//把 -c[i]-d[j]的值在sum里二分查找 } } printf("%d ",ans); } return 0; }