Week4作业 B - 四个数列
问题描述:
给四个数列A,B,C,D,每个数列各有n个数(𝑛 ≤ 4000)。从每个数列中各取出一个数,问有多少种方案使得4个数的和为0。当一 个数列中有多个相同的数字的时候,把它们当做不同的数对待。其中,数字不超过 2 的 28 次方。
思路:
枚举A+B的和并存储,然后枚举C+D的和,当ci+di的相反数在ai+bi中出现过k次时,答案就加k。
如何找ci+di的相反数出现过几次?起初的方案是用桶,但是2^28=1e8,而且主要的问题是有可能出现负数,但数组下标用负数就不和谐了。
用map的话,每次查询logn,超时!!?但是为什么二分不超时?都是logn啊
我尝试使用之前讲过的unordered_map,并且包含了头文件map和unordered_map,本地测试样例通过,但是VJ上C++和G++都CE,失败的尝试。
最后用二分求了左右边界,然后相减得出的结果。
后来又查阅了STL,精简(偷懒)了一下代码
代码:
#include <cstdio> #include <iostream> #include <vector> #include <map> #include <algorithm> //#include <unordered_map> using namespace std; const int MAXN=5e5+5; vector<int> sum; int a[MAXN],b[MAXN],c[MAXN],d[MAXN]; int find(int x) //在有序序列中找x出现的次数 { int l=0,r=sum.size()-1,lans=-1,rans=-1; while(l<=r) //找第一次出现 { int mid=(l+r)/2; if(sum[mid]==x) lans=mid,r=mid-1; else if(sum[mid]>x) r=mid-1; else l=mid+1; } if(lans==-1) return 0; l=0,r=sum.size()-1; while(l<=r) //找最后一次出现 { int mid=(l+r)/2; if(sum[mid]==x) rans=mid,l=mid+1; else if(sum[mid]>x) r=mid-1; else l=mid+1; } return rans-lans+1; } int main() { int n; cin>>n; for(int i=0;i<n;i++) scanf("%d %d %d %d",a+i,b+i,c+i,d+i); int ans=0; for(int i=0;i<n;i++) for(int j=0;j<n;j++) { sum.push_back( a[i]+b[j] ); } sort( sum.begin(),sum.end() ); for(int i=0;i<n;i++) for(int j=0;j<n;j++) { ans+=find( -( c[i]+d[j] ) ); } cout<<ans<<endl; return 0; }
总结:
1、静态区开大数组, 一般的写法是:
const int MAXN=1e6+5; int v[MAXN];
2、STL中的二分查找
①binary_search:
//存在VAL则返回true,否则返回false,区间左闭右开[first,last) bool binary_search (ForwardIterator first, ForwardIterator last, const T& val)
②lower_bound:
//二分求下界,返回第一个大于等于val的位置 ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val)
③upper_bound:
//二分求上界,返回第一个大于val的位置 ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
当val不存在时,lower_bound()和upper_bound()返回相同的值,都是第一个大于val的位置
3、用STL改写了的代码:
#include <cstdio> #include <iostream> #include <vector> #include <map> #include <algorithm> //#include <unordered_map> using namespace std; vector<int> sum; int a[5005],b[5005],c[5005],d[5005]; int find(int x) //在有序序列中找x出现的次数 { //不能直接用int *p=upper_bound(sum.begin(),sum.end(),x); 因为返回的不是int*类型 return upper_bound(sum.begin(),sum.end(),x)-lower_bound(sum.begin(),sum.end(),x); } int main() { int n; cin>>n; for(int i=0;i<n;i++) scanf("%d %d %d %d",a+i,b+i,c+i,d+i); int ans=0; for(int i=0;i<n;i++) for(int j=0;j<n;j++) { sum.push_back( a[i]+b[j] ); } sort( sum.begin(),sum.end() ); for(int i=0;i<n;i++) for(int j=0;j<n;j++) { ans+=find( -( c[i]+d[j] ) ); } cout<<ans<<endl; return 0; }