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

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

Time limit15000 ms
Case time limit5000 ms
Memory limit228000 kB
OS Linux
解题思路:如果用4个for循环直接查找,由于O(n4)不满足题目对时间的要求,因此需要对算法进行适当优化,可以采用二分法将时间复杂度降到O(n2).

find()函数是一个二分查找的函数实现,本题代码实现时先将a[],b[]两个数组的和求出,存入一个n*n的数组p[]中,并将其由小到大排序,定义sum[]数组存储p[]中每个数在p[]中出现的次数,再利用两个for循环将c[i],d[j]求和-s,利用二分查找(find()函数)找到与s相等的p[k],ans+=sum[i],如果未找到则继续找c[i]+d[j+1]或c[i+1]+d[1]的相反数,直到查找完毕,输出ans。
实现代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;

int n;
int a[4100], b[4100], c[4100], d[4100];
int sum[16100000],p[16100000],p2[16100000];
//二分查找
int find(int x, int num)
{
    int l = 1, r = num, ans = -1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (p[mid] == x)
        {
            ans = mid;
            //      l = mid + 1;
            r = mid - 1;
        }
        else if (p[mid] > x)
            r = mid - 1;
        else
            l = mid + 1;
    }
    return ans;
}
int main()
{

    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d %d %d %d", &a[i], &b[i], &c[i], &d[i]);//cin >> a[i] >> b[i] >> c[i] >> d[i];
    }
    int sum1 = 0, k = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            p[k] = a[i] + b[j];
            k++;
        }
    }
    int cnt = 0;
    int f = n * n;
    sort(p + 1, p + f + 1);
    int temp = 0, num = 0x7fffffff;
    p[0] = 0x7fffffff;
//sum[i]用来存储p[]中值为p[i]数的个数
    for (int i = 1; i <= f; i++)
    {
        num = p[temp];
        if (p[i] != num)
        {
            sum[i]++;
            temp = i;
        }
        else
        {
            sum[temp]++;
            sum[i]++;
        }
    }
    int t = 0;
//让c[i]与d[i]相加,利用find()函数找出与-(c[i] + d[j])相等的数的下标
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            int s = -(c[i] + d[j]);
            t = find(s, temp);//temp是需要查找的最大位置
            if (t != -1)
                cnt += sum[t];
            else if (t == -1)
                continue;
        }
    }
    cout << cnt << endl;
    return 0;
}
/*
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
*/

总结:合理使用二分法可以有效将程序优化,降低求解所需时间。如果实验数据比较多,尽量用scanf(),不然容易超时,同时在定义数组的时候后一定要舍得,否则会TLE。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,428评论 0 2
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,734评论 0 3
  • 1. file n. 文件;v. 保存文件2. command n. 命令指令3. use v. 使用用途4. p...
    喵呜Yuri阅读 782评论 0 4
  • 首页 资讯 文章 资源 小组 相亲 登录 注册 首页 最新文章 IT 职场 前端 后端 移动端 数据库 运维 其他...
    Helen_Cat阅读 3,955评论 1 10
  • 一年之初第一天,以爬山开始 虽然独自一人,但也挺好,不为目的,只为沿途风景 不用太着急,只是慢慢向上走 相伴的,只...
    顽石_Shu阅读 567评论 9 13