从一道例题开始

有一片大海,大海中隐藏着若干陆地,当海平面下降的时候漏出的连续陆地会组成一个小岛,问题:当海平面处于多高的时候才能使漏出的小岛数量最多?数量是多少?

差分法解释

一个数组$A[1,2,3,4,5,6,7,8,9]$他的差分数组就是$A[i]-A[i-1]$,为方便计算我们给数组$A$头部插入一个0,变为$A[0,1,2,3,4,5,6,7,8,9]$,则其差分数组为$B[0,1,1,1,1,1,1,1,1,1]$(头部0也是单独插入的),差分数组又一个很重要的性质,$\sum_0^nB[i]=A[n]$,即$B$的前缀和等于$A$的相应下表的值,这有什么用呢?例如我们想给$A[2:6]$区间+1,除了直接在$A$中循环,还可以把$B$更改为$[0,1,2,1,1,1,1,0,1,1]$,即$B[2]+1,B[7]-1$,然后在按照前缀和的形式加回去,也能算出来,这就是差分法,上题就是一个经典案例。

暴力法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
vector<int> nums = {6, 5, 7, 1, 4, 2, 4};
nums.insert(nums.begin(), 0);
nums.push_back(0);
int max_value = *max_element(nums.begin(), nums.end());
vector<int> levels(max_value + 1);//海平面
for(int i = 1; i < nums.size() - 1; ++i)
{
if(nums[i] > nums[i - 1])
{
for(int j = nums[i - 1] + 1; j <= nums[i]; ++j)
{
levels[j]++;
}
}
}
cout << *max_element(levels.begin(), levels.end());
return 0;
}

很明显,复杂度$O(n^2)$

差分法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void add_diff(vector<int>& diff, int low, int high)
{
diff[low]++;
diff[high + 1]--;
}

int main()
{
vector<int> nums = {6, 5, 7, 1, 4, 2, 4};
nums.insert(nums.begin(), 0);
nums.push_back(0);
int max_value = *max_element(nums.begin(), nums.end());
vector<int> diff(max_value + 1);//差分数组
for(int i = 1; i < nums.size() - 1; ++i)
{
if(nums[i] > nums[i - 1])
{
add_diff(diff, nums[i - 1] + 1, nums[i]);
}
}
int maxn = 0, pre = 0;
for(int p = 1; p <= max_value; p++)
{
pre += diff[p];
maxn = max(maxn, pre);
}
cout << maxn;
return 0;
}

复杂度$O(n)$