洛谷P1102 A-B 数对C++题解(改进二分+map,解析详细)

 原题链接:P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1102

题目描述:

给出一串长度为N的正整数数列以及一个正整数 C,要求计算出所有满足A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)

输入格式:

输入共两行。

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

输出格式:

满足A−B=C 的数对的个数。

数据限制:

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

对于 100%的数据,1≤N≤2×10^5,0≤ai​<2^30,1≤C<2^30。

解题思路:

        暴力解法当然是在数组中遍历查找满足条件的A和B,时间复杂度为O(N^2),可以得到75%的分数。但对于剩下25%的测试点显然是通过不了的。

        既然是查找问题,自然可以通过二分搜索进行优化。思路就是:先对数组进行应用快速排序,将其变为有序的序列,然后在寻找满足A-B=C的所有数对个数时,遍历所有A值,通过改进的二分查找满足条件的B值。此外,由于题目要求在原数组中不同位置的数字一样的数对算不同的数对,即查找到的满足条件的B有多少个相同的值,那么数对的总数也要加上多少个。所以使用一个map来存储某个数值在输入数组中对应有多少个相同的值

改进的二分查找函数体如下(数列已提前从小到大排列好):

mapm;
void binary(int left,int right,int num) {
	if (left > right) return;
	int mid = (left + right) / 2;
	if (num - a[mid] == c) {
		//加上这个数重复出现了多少次
		cnt += m[a[mid]];
		return;
	}
	//如果差值比C大,应该往右找
	else if (num - a[mid] > c)  binary(mid + 1, right, num);
	//如果差值比C小,应该往左找
	else binary(left, mid - 1, num);
}

一、参数含义:left和right代表当前二分搜索的左右边界。nums代表当前的A值

二、累加条件:如果num-a[mid]==c(即A−B=C)时,表明当前的a[mid]满足条件,那么总数目则累加上该满足条件的与B相同值的个数

        (tips:如果题目改为,不同位置的数字一样的数对算相同的数对,此时只需要cnt++即可)

三、递归方向:

       1. 如果num-a[mid]>c,则说明当前的a[mid]还太小,应该往数值更大的方向查找,此处应笔者提前进行了从小到大排序,故应向右继续二分查找。

        2.同理,如果num-a[mid]

(无论C的值正或负,递归查找方向都如上)

代码:

#include
#include
#include
using namespace std;
long long a[200001],n,c;
long long cnt;
mapm;
void binary(int left,int right,int num) {
	if (left > right) return;
	int mid = (left + right) / 2;
	if (num - a[mid] == c) {
		//加上这个数重复出现了多少次
		cnt += m[a[mid]];
		//cout << num << " " << a[mid]<<" "< c)  binary(mid + 1, right, num);
	//如果差值比C小,应该往左找
	else binary(left, mid - 1, num);
}
//思路:寻找满足A=B=C的所有数对个数,遍历所有A值,通过二分查找满足条件的B值
int main() {
	cin >> n >> c;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		m[a[i]]++;
	}
	sort(a, a + n);
	//固定被减数,二分查找减数
	for (int i = 0; i < n; i++) {
		binary(0, n - 1, a[i]);
	}
	cout << cnt;
	return 0;
}

        

你可能感兴趣的:(数据结构与算法,c++,算法)