程序设计思维 week4 作业B-四个数列

题目

ZJM 有四个数列 A,B,C,D,每个数列都有 n 个数字。ZJM 从每个数列中各取出一个数,他想知道有多少种方案使得 4 个数的和为 0。
当一个数列中有多个相同的数字的时候,把它们当做不同的数对待。

Input

第一行:n(代表数列中数字的个数)(1≤n≤4000)。
接下来的 n 行中,第 i 行有四个数字,分别表示数列 A,B,C,D 中的第 i 个数字(数字不超过 2 的 28 次方)。

Output

输出不同组合的个数。

Sample Input

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

Sample Output

5

思路

如果直接枚举ABCD中的数字,则复杂度为O(n^4),复杂度过高。这样做的想法是计算A+B+C+D=0。
为了少枚举点,降低复杂度,可以枚举A和B,然后再枚举C和D,则复杂度为O(n^2)。而这样做的想法是计算A+B=-(C+D)。
首先枚举A和B,计算A+B的和,并储存在数组e[]中,并使其为有序数组。然后再枚举C和D,计算C+D的相反数在e[]中出现多少次。在计算相反数出现多少次时,采用二分法。
进行二分时,左右边界l、r分别取数组e[]的左右边界,当l<r时,循环,取mid=(l+r)/2位置的值,若为要找的值,则查看其左右是否也是要找的值,直到找完其左右所有符合要求的点,返回找到的点的数目;若比要找的值小,l更新为mid+1;若比要找的值大,r更新为mid-1。

代码

#include <cstdio>
#include <algorithm>
using namespace std;

int a[4005],b[4005],c[4005],d[4005],e[4000*4000+5];
int n;

int find(int x){
    int l=0,r=n*n,leftx=0,rightx=0;
    while(l<=r){
        int mid=(l+r)/2;
        if(e[mid]==x){
            while((mid+rightx)<n*n&&e[mid+rightx]==x){
                rightx++;
            }
            while((mid-leftx)>=0&&e[mid-leftx]==x){
                leftx++;
            }
            return leftx+rightx-1;
        }
        else if(e[mid]<x)
            l=mid+1;
        else if(e[mid]>x)
            r=mid-1;
    }
    return 0;
}

int main() {
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
    }
    int k=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            e[k]=a[i]+b[j];
            k++;
        }
    }
    int sum=0;
    sort(e,e+n*n);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            sum+=find(-(c[i]+d[j]));
        }
    }
    printf("%d",sum);
    return 0;
}

题目链接

发布了24 篇原创文章 · 获赞 2 · 访问量 435

猜你喜欢

转载自blog.csdn.net/weixin_43805228/article/details/104978161