WEEK4作业B 四个数列 POJ-2785

题目:
ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。
请你帮帮他吧!
输入:
第一行:n(代表数列中数字的个数) (1≤n≤4000)
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)
输出:
输出不同组合的个数。

样例输入:
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
样例输出:
5

样例解释: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

如果用暴力的方法,直接遍历计算A+B+C+D==0,复杂度高达O(N^4),数据范围为1~4000,肯定会超时。本题可以采用二分的方法,计算A+B=-(C+D),降低复杂度。
先计算A+B和C+D,得到数组ab,cd,然后调用sort函数将这两个数组分别排序。之后,循环在数组cd中,分别用二分搜索找到等于-ab[i]的元素的最大序号和最小序号。根据最大序号和最小序号对答案进行累加。
搜索等于ele的元素的最小的序号:

int searchMin(int *x,int ele,int num)//如果没找到与ele相等的元素则返回-1
{
 int l=0,r=num,mid;
 int ans=-1;
 while(l<=r)
 {
  mid=(l+r)/2;
  if(x[mid]==ele)
  {
   ans=mid;
   r=mid-1;
  }
  else if(x[mid]>ele)
  {
   r=mid-1;
  }
  else if(x[mid]<ele)
  {
   l=mid+1;
  }
 }
 return ans;
}

搜索等于ele的最大的元素的序号:

int searchMax(int *x,int ele,int num)//如果没找到与ele相等的元素则返回-1
{
 int l=0,r=num,mid;
 int ans=-1;
 while(l<=r)
 {
  mid=(l+r)/2;
  if(x[mid]==ele)
  {
   ans=mid;
   l=mid+1;
  }
  else if(x[mid]>ele)
  {
   r=mid-1;
  }
  else if(x[mid]<ele)
  {
   l=mid+1;
  }
 }
 return ans;
}

关键部分:

int first,last;
 int ans=0; 
 for(int i=0;i<n*n;i++)
 {
  first=searchMin(cd,-1*ab[i],n*n-1);//数组cd中等于-ab[i]的元素的最小序号
  last=searchMax(cd,-1*ab[i],n*n-1);//数组cd中等于-ab[i]的元素的最大序号
  if(first==-1&&last==-1)//说明没有找到等于-ab[i]的元素
  {
   continue;
  }
  else if( (first==-1&&last!=-1) || (first!=-1&&last==-1) )//等于-ab[i]的元素只有一个
  {
   ans=ans+1;
  }
  else
  {
   ans=ans+last-first+1; //有多个元素
  }
 }

以下是完整代码:

#include<iostream>
#include<algorithm>
using namespace std;
int a[5000],b[5000],c[5000],d[5000];
int ab[30000000],cd[30000000];
int n;

int searchMin(int *x,int ele,int num)
{
 int l=0,r=num,mid;
 int ans=-1;
 while(l<=r)
 {
  mid=(l+r)/2;
  if(x[mid]==ele)
  {
   ans=mid;
   r=mid-1;
  }
  else if(x[mid]>ele)
  {
   r=mid-1;
  }
  else if(x[mid]<ele)
  {
   l=mid+1;
  }
 }
 return ans;
}
int searchMax(int *x,int ele,int num)
{
 int l=0,r=num,mid;
 int ans=-1;
 while(l<=r)
 {
  mid=(l+r)/2;
  if(x[mid]==ele)
  {
   ans=mid;
   l=mid+1;
  }
  else if(x[mid]>ele)
  {
   r=mid-1;
  }
  else if(x[mid]<ele)
  {
   l=mid+1;
  }
 }
 return ans;
}
int main()
{
 cin>>n;
 for(int i=0;i<n;i++)
 {
  cin>>a[i]>>b[i]>>c[i]>>d[i];
 }
 
 int cnt=0;
 for(int i=0;i<n;i++)
 {
  for(int j=0;j<n;j++)
  {
   ab[cnt++]=a[i]+b[j];
  }
 }
 
 cnt=0;
 for(int i=0;i<n;i++)
 {
  for(int j=0;j<n;j++)
  {
   cd[cnt++]=c[i]+d[j];
  }
 }
 sort(ab,ab+n*n);
 sort(cd,cd+n*n);

int first,last;
 int ans=0; 
 for(int i=0;i<n*n;i++)
 {
  first=searchMin(cd,-1*ab[i],n*n-1);
  last=searchMax(cd,-1*ab[i],n*n-1);
  if(first==-1&&last==-1)
  {
   continue;
  }
  else if( (first==-1&&last!=-1) || (first!=-1&&last==-1) )
  {
   ans=ans+1;
  }
  else
  {
   ans=ans+last-first+1; 
  }
 }
 cout<<ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值