洛谷题解——P1102:A-B 数对

题目相关

题目链接

洛谷,https://www.luogu.com.cn/problem/P1102

我的OJ,http://47.110.135.197/problem.php?id=4759

题目描述

给出一串数以及一个数字 C,要求计算出所有 A - B = C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个整数 N, C。

第二行,N 个整数,作为要求处理的那串数。

输出格式

一行,表示该串数中包含的满足 A−B=C 的数对的个数。

输入样例

4 1
1 1 2 3

输出样例

3

数据规模

对于 75% 的数据,1 ≤ N ≤ 2000。

对于 100% 的数据,1 ≤ N ≤ 2×10^5。

保证所有输入数据都在 32 位带符号整数范围内。

题目分析

题意分析

在一个数列中,找出所有 A-B=C 的组合。

样例数据分析

有 4 个数据的数列,从中找出所有差值为 1 的数据组合。输入的数列为:1 1 2 3。我们可以知道:

1、第三个数据 2 减去第一个数据 1,两者之差为 1。

2、第三个数据 2 减去第二个数据 1,两者之差为 1。

3、第四个数据 3 减去第三个数据 2,两者之差为 1。

因此总计有 3 组数据。

数据规模分析

1、根据题目描述,保证所有输入数据都在 32 位带符号整数范围内,因此用 int 可以表示。

2、N 的最大范围是 2e5。这个有什么用?只有当数据 A 的总个数和数据 B 的总个数只差为 1 的时候,答案是最大的。就是我们如果有这样一组输入数据:

10 1
1 1 1 1 1 2 2 2 2 2

如果上面的数据,有 5 个 1、5 个 2,这样最后的答案为 5*5=25。

也就是说,我们的输出结果最大的范围是 (n/2)*(n/2),也就是 10^5*10^5=10^10。

说明我们需要用 long long 或者 unsigned long long 来表示结果。

注意:在乘积的过程中,可能导致 int 溢出。

模拟算法

算法思路

1、读入数。

2、统计每个数出现的数量。

3、排序。

4、去重。

5、计算结果。

该算法的核心是如何对所有的数据个数进行统计。

方法一:采用数组来统计,数组的大小是 2^32 个。这个方法明显比较浪费。

方法二:使用 STL 中的 map 来统计。

AC 参考代码

/*
OJ:MYOJ
题号:4759
题目:A-B 数对
地址:http://47.110.135.197/problem.php?id=4759
*/
#include <bits/stdc++.h>

const int MAXN = 2e5+2;
int arr[MAXN];

int main() {
    std::map<int, int> myMap;
    int n;
    int c;
    scanf("%d %d", &n, &c);

    int i;
    for (i=0; i<n; i++) {
        scanf("%d", &arr[i]);
        myMap[arr[i]]++;//当前数的个数++
    }
	
    std::sort(arr, arr+n);//排序
    n = std::unique(arr, arr + n) - arr;//去重

    //统计
    unsigned long long ans = 0;
    for (i=0; i<n; i++) {
        if ((myMap[arr[i]] && myMap[arr[i]-c])) {
            ans += ((unsigned long long)myMap[arr[i]] * myMap[arr[i]-c]);
        }
    }
    printf("%llu\n", ans);

    return 0;
}

细节讲解

1、我们必须排序。因为题目没有告诉我们数据是有序的。

2、通过统计个数后,重复的元素就可以删除了。

3、注意计算 ans 的时候,先要将 int 的数据类型强制转换为 unsigned long long,否则两个 int 乘法可能导致溢出。比如输入有 2e5个数据,C 为 1,数列中有 1e5 个 1 和 1e5 个 2。这样我们的答案是 10^5*10^5=10*10,而样例代码中的 map 中保存的是 int 类型,将导致数据溢出。

二分查找

算法思路

1、读入数。

2、排序。

3、从头开始二分查找。我们有了 A 和 C,就可以在数列中查有几个 B。也就是查找数据 B 的左下界和右上界。

AC 参考代码

/*
OJ:MYOJ
题号:4759
题目:A-B 数对
地址:http://47.110.135.197/problem.php?id=4759
*/
#include <bits/stdc++.h>

const int MAXN = 2e5+2;
int arr[MAXN];

int main() {
    int n;
    int c;
    scanf("%d %d", &n, &c);

    int i;
    for (i=0; i<n; i++) {
        scanf("%d", &arr[i]);
    }
	
    std::sort(arr, arr+n);//排序

    //统计
    unsigned long long ans = 0;
    for (i=0; i<n; i++) {
        int b = arr[i]-c;
        int lo = std::lower_bound(arr, arr+n, b) - arr;
        int hi = std::upper_bound(arr, arr+n, b) - arr;
        if (lo!=hi || arr[lo]==b) {
            ans += (hi-lo);
        }
    }
    printf("%llu\n", ans);

    return 0;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值