【2015南海小学】差(c++,最用心的题解)

文章讲述了如何处理一个编程题,寻找给定有序整数数组中满足差值条件的数对,面对大数据范围时,作者提出了一种不同于常规方法的解决方案,利用特殊桶结构进行去重并优化时间复杂度。
摘要由CSDN通过智能技术生成
题目描述

      楠楠在网上刷题,感觉第一题:求两数的和(A+B Problem)太无聊了,于是增加了一题:A-B Problem,难倒了一群小朋友,哈哈。        //楠楠真的欠(╬▔皿▔)
      题目是这样的:给出N 个从小到大排好序的整数,一个差值C,要求在这N个整数中找两个数A 和B,使得A-B=C,问这样的方案有多少种?
      例如:N=5,C=2,5 个整数是:2 2 4 8 10。答案是3。具体方案:第3 个数减第1 个数;第3 个数减第2个数;第5 个数减第4 个数。

输入

从文件 dec.in 中读入数据。

   第一行2 个正整数:N,C。
   第二行N 个整数:已经有序。注意:可能有相同的。

输出

输出到文件 dec.out 中。

一个整数,表示该串数中包含的所有满足A-B=C 的数对的方案数。

样例输入 
4 1
1 1 2 2

样例输出 

4
数据范围限制

5 个数据:N 的范围是[1…1,000]。
5 个数据:N 的范围是[1…100,000]。
所有数据:
C 的范围是[1…1,000,000,000]。
N 个整数中每个数的范围是:[0…1,000,000,000]。


这道题眨眼一看,双重循环秒了 或 用个桶就行了

但一看数据范围,直接炸了

每个数的范围[1~1000000000],那N也的范围也是[1~1000000000],甚至更大

双重循环会时间超限,桶会空间超限

于是我得用一种 与众不同 的方法

我们来看一下题目的样例

5 2  //5个数  差值为2
2 2 4 8 10

我们可以定义一个 ij 枚举每一个数(j>i),题目上说输入一定是个从小到大的数列,那我们只需要判断:第 个数 - 第 个数是否为 (C为两个数的差值,见题目),是的话就 ans+1,不是的话 j+1 到下一个数。

见下表

10
ij

j 个数 2 - 第 i 个数 2 = 0  ,差值不对,找下一个数, j+1

10
ij

j 个数 - 第 i 个数 2 = 2  ,差值相同,j 不变,i+1 ans+1

10
ij

j 个数 - 第 i 个数 2 = 2  ,差值相同,j 不变,i+1 ans+1

10
i  j

j 个数 4 - 第 i 个数 4 = 0  ,差值不对,找下一个数, j+1

10
ij

j 个数 8 - 第 i 个数 4 = 4  ,差值不对且差值大于C,找下一个数, i+1,j+1

当两数差值大于 C 时,j 再 +1 只会让差值更大,所以直接跳过,i+1(这是一个从小到大的数列)

10
ij

j 个数 10 - 第 i 个数 8 = 2  ,差值相同,j 不变,i+1,ans+1

10
i j

j 个数 10 - 第 i 个数 10 = 0  ,差值不对,结束

所以总结一下  差值相同i+1    差值不同j+1(若差值比C大 i+1)


理论来说,我们只用这样

for(long long i=1;i<=cnt;i++)	//i+1  枚举 时间复杂度为 O(cnt) 
{
	for(long long j=s;j<=cnt;j++)	//从s开始 见第22行 
	{
		if(t[j]-t[i]==c)	//两数差值为 C 
		{
			ans++; //ans+1 
			s=j+1;	//j+1 用s记录 
			break;
		}
		if(t[j]>t[i]+c) break;	//两数差值大于 C 直接退出 
	}
}

但我们似乎少算了一个问题

当N为11 C为5 数列为 3 3 3 8 8 8 8 8 8 9 9

我们代入总结的方法,j 只会到第一个8然后就不会向后,直到 i 也到了8 才向后,这样就会漏下很多的正确结果

因此我们得把它们装进里面达到去,达咩 桶不是会爆吗?

但我这个桶不是一般的桶(存下标的),而是王维诗里自己创建的按顺序的桶,LOOK

我们来存一下样例

下标123456789
数字389
数量362

像这样,既能做到去重,又能保存数量,而且节约空间。让我们把装进桶的数列代入总结的方法,

数字389
数量362
ans=0ij

j 个数 8 - 第 i 个数 3 = 5  ,差值相同,j 不变,i+1,ans+6*3(差值相同的情况下,有6个8,3个3,差值相同就有6*3=18个,想一想,是不是很妙)

数字389
数量362
ans=18i  j

j 个数 8 - 第 i 个数 8 = 0  ,差值不对,j+1

数字389
数量362
ans=18ij

j 个数 10 - 第 i 个数 10 = 0  ,差值不对,结束


代码实现,见下

#include<bits/stdc++.h>
using namespace std;
long long n,c,a,b,t[100001],sum[100001],ans,cnt,s;
//定义数列长度n 差值c 存数字的t 存数量的sum 结果ans 
int main()
{
	cin>>n>>c;	//输入 n 和 数的差值 
	for(long long i=1;i<=n;i++)    //人工桶
	{
		cin>>a;
		if(a!=b || i==1) ++cnt;		 //前后两数是否相同 不是就要把新的数入桶 见第14行 
		t[cnt]=a;	//入桶  t表示数字 
		sum[cnt]++;	//统计数量 	sum表示数量 
		b=a;	//保存上一个数,用于跟新的数比较 
	}
	for(long long i=1;i<=cnt;i++)	//i+1  枚举 时间复杂度为 O(cnt) 
	{
		for(long long j=s;j<=cnt;j++)	//从s开始 见第22行 
		{
			if(t[j]-t[i]==c)	//两数差值为 C 
			{
				ans+=sum[i]*sum[j]; //ans+数量*数量 
				s=j+1;	//j+1 用s记录
				break;
			}
			if(t[j]>t[i]+c) break;	//两数差值大于 C 直接退出让i操作 
		}
	}
	cout<<ans;
	return 0;
}

完结撒花!!!!!!!!!!!!!!!!!!!

  • 33
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值