【高效算法设计-中途相遇法】4 Values whose Sum is 0 哈希表

针对四数求和问题,提出了一种高效的解决方案。通过将问题分解为两部分并利用哈希表来加速查找过程,成功地将时间复杂度降低至O(n^2)。文章详细介绍了中途相遇法的应用及哈希表的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4 Values whose Sum is 0
Time Limit: 15000MS Memory Limit: 228000K
Total Submissions: 16375 Accepted: 4748
Case Time Limit: 5000MS

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).

Source



题意:本题题意很简单,给定4组数,每组有n个数,从每组数中各取一个数,问有多少种取法使得取出的四个数之和为0

思路:很容易想到用暴力枚举的方法,时间复杂度为O(n^4),必然超时,而对于这种从集合中选组合类的问题,我们通常采用一种方法,即 中途相遇法

中途相遇法:就是从两个方向向中间靠拢的一种做法,如双向BFS,对于此题,我们可以把前两组的组合和后两组的组合分开计算,然后再看是否之和为0,即我们先计算前两组数的和,利用数据结构存储起来,再计算后两组数的和,进行查询,这样时间复杂度就降低到O(n^2*查询的时间复杂度),由于本题数据范围较大,无法使用vis数组进行O(1)的查询,因此只能考虑使用map或者hash表

由于map的代码复杂度较低,时间复杂度为O(n^2*logn),我们可以先尝试用map写一个demo程序
使用map的代码如下

#include<cstdio>
#include<cstring>
#include<map>

using namespace std;


map<int,int>MAP;
int a[5][5000];

int main()
{
    int n,i,j,tt,ans;


    while(~scanf("%d",&n))
    {
        ans=0;
        MAP.clear();
        for(j=1;j<=n;j++)
            for(i=1;i<=4;i++)
                scanf("%d",&a[i][j]);


        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                tt=a[1][i]+a[2][j];
                MAP[tt]++;
            }

         for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                tt=a[3][i]+a[4][j];
                ans+=MAP[-tt];
            }

            printf("%d\n",ans);

    }

    return 0;
}

很遗憾,使用map会超时,于是我们考虑使用hash,实现hash的方法主要使用开放寻址法或者哈希邻接表
利用head[]数组和next[]数组可以轻松完成哈希邻接表,我们使用除法取余作为哈希函数,head数组的大小为素数mod的大小, 这里素数取得越大,发生碰撞的可能性就越小,next[]的大小为哈希表中一共需要存入数据个数的大小,即前两组数的组合个数,n^2

哈希AC代码如下:
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

//const int mod=3999971;
const int mod=10000007;

int head[mod+50];
int next[16000000+50];

int a[5][5000];
int res[16000000+50];
void insert_hash(int x)
{

    int v=abs(res[x]%mod);
    next[x]=head[v];
    head[v]=x;
}

int find_hash(int x)
{
    int cnt=0;
    int t=abs(x);
    int u=head[t%mod];
    while(u!=-1)
    {
        if(res[u]==x)
            cnt++;
        u=next[u];
    }
    return cnt;
}
int main()
{
    int n,i,j,tt,ans,k;


    while(~scanf("%d",&n))
    {
        ans=0;
        memset(head,-1,sizeof head);
        for(j=1;j<=n;j++)
            for(i=1;i<=4;i++)
                scanf("%d",&a[i][j]);

        k=0;
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                tt=a[1][i]+a[2][j];
                res[++k]=tt;
                insert_hash(k);
            }


         for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                tt=a[3][i]+a[4][j];
                ans+=find_hash(-tt);
            }

            printf("%d\n",ans);

    }

    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值