POJ2785 4 Values whose Sum is 0(二分的力量)



Description

The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .

Input

The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 2 28 ) that belong respectively to A, B, C and D .

Output

For each input file, your program has to write the number quadruplets whose sum is zero.

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

Hint

Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).

题目大意:给一个4*N 的数列,在每一列中找一个数,使这四个数加起来的和为0,问有多少种组合方法。
之前一直不相信二分查找的力量,做了这道题过后算是彻底服了。举个例子,你要从一个元素有10的九次方个的数组中找出一个元素,for循环的话有十亿级别,而二分查找只用9log2(10),不到45。也就是说for循环要用十亿秒(多少年了)的问题二分法一分钟不到就可以做出来。
由于数据量巨大(N<=4000),若用四重for循环的话是4的4000次方,铁定超时。可以先将前两列和后两列的总共有可能的和求出来,分别组成cnt1,cnt2数组,再对cnt1排序,再用每一个cnt2中的元素对cnt1进行二分查找,即(cnt1[mid]+cnt2[i]==0),找到后从Mid开始往两边搜,搜出此cnt2的方案总数。中间有许多细节需要处理好,尤其是二分边界的处理,这里就不赘述,请看代码。

#include <iostream>
#include <stdio.h>
#include <algorithm>

using namespace std;

int cnt1[16000002],cnt2[16000002];

int main()
{
    int n,i,a[4002],b[4002],c[4002],d[4002],left,right,mid,ans=0,total,j,flag,k;
    scanf("%d",&n);
    for (i=0;i<=n-1;i++)
    {
        scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
    }
    total=0;
    for (i=0;i<=n-1;i++)
    {
        for (j=0;j<=n-1;j++)
        {
            cnt1[total]=a[i]+b[j];
            cnt2[total]=c[i]+d[j];
            total++;
        }
    }
    sort(cnt1,cnt1+n*n);
    for (i=0;i<=n*n-1;i++)
    {
        left=0;
        right=n*n-1;
        flag=0;
        while(right-left>=2)
        {
            mid=(left+right)/2;
            if (cnt2[i]+cnt1[mid]==0)
            {
                for(j=mid+1;cnt1[j]==cnt1[mid]&&j<=n*n-1;j++)
                {
                    ans++;
                }
                for(k=mid-1;cnt1[k]==cnt1[mid]&&k>=0;k--)
                {
                    ans++;
                }
                ans++;
                flag=1;
                break;
            }
            else if(cnt2[i]+cnt1[mid]>0)
            {
                right=mid;
            }
            else if (cnt2[i]+cnt1[mid]<0)
            {
                left=mid;
            }
        }
        if (cnt2[i]+cnt1[right]==0&&flag==0)
        {
            ans++;
        }
        if (cnt2[i]+cnt1[left]==0&&flag==0)
        {
            ans++;
        }
    }
    printf("%d",ans);
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值