【每日一记】算法学习2——hash与一维前缀和

一、hash

1.hash

这个东西乍一看似乎有点难,实际上也就是将具有相同值的数据加进数组对应下标中,并求值。如果这么说比较难懂,那么我来做一个比喻:将数组的每一个下标变成一个桶,将每一个数据变成一个球。我们需要做的就是将对应的球扔进对应的桶中,如下图:
球和水桶
将球扔进与球的数字对应的桶:
球进桶
最后进行统计:
统计球数
这就对应了哈希的方法。这个方法适用于给定数据求个数,或查询每个数据出现多少次的时候很好用,但仅限于整数的情况(毕竟是用下标代表数据)。

2. 典型例子

分数查询
这是一个非常典型的例子。我们可以用一个数组来表示每个分数的人数个数。这道题也很好的诠释了为什么我没有写仅限于非负整数,而是仅限于整数。 前面的不需要多说,就是在存储数据时,只需要将所有的分数全部+1000就可以了,这样-1000就变为了0,1000变为了2000。

3.AC代码

#include <iostream>
#include <cstring>
using namespace std;

int num[3003];
int main()
{
	ios::sync_with_stdio(0); //输入输出优化
	
	memset(num, 0, sizeof(num));//其实没有必要,全局变量自动设为0
	
	int n;
	cin >> n;
	
	for (int i = 1; i <= n; i ++)
	{
		int c;
		cin >> c;
		num[c+1000] ++;    //此分数人数加1
	}
	
	int q;
	cin >> q;
	
	for (int i = 1; i <= q; i ++)
	{
		int want;
		cin >> want;
		cout << num[want+1000] << " ";
	}
	
	return 0;
}

二、一维前缀和

1.一维前缀和

这个算法适用于区间去和,想要快速取区间和,就可以采用这个算法。下面是它的推导过程:
sum[i] = a[1] + a[2] + … + a[i-1] + a[i]
∵a[1] + a[2] + … + a[i-1] = sum[i-1]
sum[i] = sum[i-1] + a[i],即为一维前缀和递推式。
当你需要知道某个区间的和时,你只需要将sum[r] - sum[l-1]就可以了。

2.在数组中截取子片段

给定一个a数组{30, 10, 50, 20, 40},要求输出它的所有连续子数组。

我们先不上程序,在纸上分析一下:
假设有三个变量l, r, len,其中l表示起点,r表示终点,len表示长度。
我们截取出a的所有子片段,并对他们进行分析:
子数组情况
第一行,len = 1, l = 1, r = 1; l = 2, r = 2; l = 3, r = 3; l = 4, r = 4; l = 5, r = 5
第二行,len = 2. l = 1, r = 2; l = 2, r = 3; l = 3, r = 4; l = 4, r = 5
第三行,len = 3, l = 1, r = 3; l = 2, r = 4; l = 3, r = 5
第四行,len = 4, l = 1, r = 4; l = 2, r = 5
第五行,len = 5, l = 1, r = 5
经过总结归纳,我们可以得出r = l+len-1, len = r-l+1。因此,想要得到所有连续子数组,只需要枚举三个变量中的任意两个,就可以得出另外一个。代码如下:

#include <iostream>
using namespace std;

int a[6] = {0, 30, 10, 50, 20, 40};
int main()
{
	int n = 5;
	for (int len = 1; len <= n; len ++)
	{
		for (int l = 1; l <= n-len+1; l ++)
		{
			int r = l+len-1;
			cout << "[";
			for (int i = l; i <= r-1; i ++)
			{
				cout << a[i] << ", ";
			}
			cout << a[r] << "]";
		}
	}
	
	return 0;
}

运行结果
可以看到,结果与先前做出的分析相符。

3. 典型示例

题目
这道题将上面讲述的两个知识点结合在了一起。具体就不进行过多的分析了,直接看代码吧:

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 505;
int a[N], sum[N];
int main()
{
	ios::sync_with_stdio(0); //输入输出优化
	
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++)
	{
		cin >> a[i];
		sum[i] = sum[i-1] + a[i];  //前缀和
	}
	
	int maxn = 0;  
	//筛选最大值(最好为0,因为如果没有更好的装饰效果就为0)
	for (int len = 1; len <= n; len ++)
	{
		for (int l = 1; l <= n-len+1; l ++)
		{
			int r = l + len - 1;  //计算终点
			maxn = max(maxn, sum[r] - sum[l-1]); //比较大小
		}
	}
	cout << maxn << endl;
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值