(STL的相关总结第八节也说的蛮清楚了的,这里就不说了)
一维前缀和
差分与前缀和的下标都从1开始,避免出现越界
算法思路:建立sum数组,sum[i]表示前i个数的总和(包括第i个),求l-r区间中数的和,则表示为sum[r]-sum[l-1]。
#include <iostream>
using namespace std;
int get_sum(int* sum, int L, int R) {
if (L <= 0) return sum[R];
return sum[R] - sum[L - 1];
}
int main() {
int n, q;
cin >> n >> q;
int* a = new int[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 构建前缀和数组
int* sum = new int [n];
sum[0] = a[0];
for (int i = 1; i < n; i++) {
sum[i] = sum[i - 1] + a[i];
}
for (int i = 0; i < n; i++) {
cout << sum[i] << " ";
}
cout << endl;
// 查询 L - R 区间和
while (q--) {
cout << "\n请输入查询的 L 和 R" << endl;
int L;
int R;
cin >> L >> R;
cout << get_sum(sum, L, R);
}
return 0;
}
一维差分
差分思想和前缀和是相反的。
#include <iostream>
using namespace std;
void mode(int* d, int L, int R, int e) {
d[L] += e;
d[R + 1] -= e;
}
int main() {
int n, m;
cin >> n >> m;
int* a = new int[n+1];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int* d = new int[n+1];
// 构建差分数列
// 在差分序列上进行操作
// 求操作后差分序列的前缀和
for (int i = 0; i < n; i++) {
d[i] = 0;
}
while (m--) {
cout << "操作的 L 和 R 以及 数" << endl;
int L;
int R;
int e;
cin >> L >> R >> e;
mode(d, L, R, e);
}
// 构建前缀和数组
int* sum = new int[n + 1];
sum[0] = d[0];
for (int i = 1; i < n; i++) {
sum[i] = sum[i - 1] + d[i];
}
for (int i = 0; i < n; i++) {
a[i] += sum[i];
cout << a[i] << " ";
}
return 0;
}
二维前缀和
“前缀和”是在一维数组中(假设该数组为 ar[] ,以ar[1] 作为第一个ar数组的第一个元素)说的,那么第i个元素的前缀和sum[i] = ar[1 ] +ar[2] + …+ar[i] ; 二维前缀和就是在二维数组中用的。
#include<iostream>
#include<cstring>
using namespace std;
int dp[2000][2000],map[2000][2000];
int main()
{
int m,n,k;//所给的矩阵是n*m的,有k组查询
cin >>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin >>map[i][j];
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)//预处理一波
for(int j=1;j<=m;j++)
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+map[i][j];
for(int i=1;i<=k;i++)//接受查询
{
int x1,x2,y1,y2;
cin >>x1>>y1>>x2>>y2;
cout <<(dp[x2][y2]+dp[x1-1][y1-1]-dp[x1-1][y2]-dp[x2][y1-1])<<endl;//O(1)查询
}
return 0;
}
题目详情:洛谷P1387 最大正方形
在一个 n×m 的只包含 0 和 1 的矩阵里找出一个不包含 0 的最大正方形,输出边长。
#include<iostream>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int a[204][204];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
cin>>a[i][j];
int qz[204][204]={0};
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
qz[i][j]=qz[i][j-1]+a[i][j];
int Min=min(n,m);
int d[204][204]={0};
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
d[i][j]=d[i-1][j]+qz[i][j];
for (int size=Min;size>=2;size--)
for (int i=1;i<=n-size+1;i++)
for (int j=1;j<=m-size+1;j++)
if (d[i+size-1][j+size-1]+d[i-1][j-1]-d[i+size-1][j-1]-d[i-1][j+size-1]==size*size) {cout<<size; retnrn 0;}//精髓
cout<<"1";
}
二维差分
- 差分算法最本质作用是通过差分矩阵随意实现前缀和子矩阵整体数值的增加和减少,差分运算和前缀和运算互为逆运算;
- 二维差分的初始化可以通过从全0矩阵构造的方法来获取
二维差分
void insert(int x1, int y1, int x2, int y2, int d)
{
b[x1][y1] += d;
b[x2 + 1][y1] -= d;
b[x1][y2 + 1] -= d;
b[x2 + 1][y2 + 1] += d;
}
int main()
{
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
// 输入原矩阵,并依次构造差分矩阵
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
scanf("%d", &a[i][j]);
insert(i, j, i, j, a[i][j]);
}
while (q--)
{
int x1, y1, x2, y2, d;
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &d);
insert(x1, y1, x2, y2, d); // 差分矩阵操作
}
// 计算差分矩阵的前缀和,也就是原矩阵的新值
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
return 0;
}