一、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;
}