一维与二维数组的前缀和与差分 (纯c实现)

    前缀和的应用场景

    

    给定一个数组, 数组大小为10^6,接下去有10^6次询问,每次询问给定一个区间[l, r],问数组第l个元素到第r个元素的和。

    转化下思维,不直接求[l, r],而是求[1, r] - [1, l-1]这样我们的时间复杂度会大大减小,总而言之就是我们可以减少遍历数组的次数但同时求出和

#include <stdio.h>
#include <string.h>
int arr[10010] = { 0 };
int main()
{
	int n = 0;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		int a;
		scanf("%d", &a);
		arr[i] = arr[i - 1] + a;
	}
	int l, r;
	scanf("%d", &l, &r);
	int value = arr[l] - arr[r - 1];
	printf("%d\n", value);
	return 0;
}

一维的差分就要稍微复杂一些,那什么是差分,为什么要使用差分,我们来举一个例子

给定一个数组, 数组大小为1000,接下去有1000次操作,每次操作给定一个区间[l, r] 和一个权值 W,区间[l, r]里面所有的数字加上W,操作完成之后,输出数组    

我们的第一思路或许是先输入数组,然后再一个个的在区间内加上权值w,但是我们这样会耗费更多的时间,题目可能也会超时

那么我们这样做,在a[l] 加上w a[r + 1] 减上w,a[l]加上w代表着[l, n]加上w, a[r + 1]减上w, 代表着[r + 1, n]减去w, 这样子所有标记完之后,做一遍前缀和就是答案,加上w后面的数在前缀和的情况下都会加上w,那么相对应的减去w的意思就是后面的数不再加上w,也就是抵消影响

#define max(a,b) (((a)>(b))?(a):(b))
#include <stdio.h>
#include <string.h>
int arr[1005] = { 0 };
int s[1005];
void suu(int a, int b, int c)
{
	s[a] += c;
	s[b + 1] -= c;
}
int main()
{
	int a, b, c;
	int n = 0;//增加次数
	int w = 0;//个数
	memset(s, 0, sizeof(s));
	scanf("%d", &w);
	for (int i = 1; i <= w; i++)
	{
		scanf("%d", &arr[i]);
	}
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d %d %d", &a, &b, &c);
		suu(a, b, c);
	}
	for (int i = 1; i <= w; i++)
	{
		s[i] += s[i - 1];
	}
	for (int i = 1; i <= w; i++)
	{
		arr[i] += s[i];
	}
	for (int i = 1; i <= w; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

这段代码可以完成以上任务并且将数组打印出来

接下来我们来顺其自然的讲讲二维数组的前缀和

我们需要用这种公式来表示二维数组的前缀和(这个的前缀和也就是那个矩形),a是前缀和的数组,ori为当前输入的那一个数组名字

#define max(a,b) (((a)>(b))?(a):(b))
#include <stdio.h>
#include <string.h>
int ori[1005][1005] = { 0 };
int a[1005][1005] = { 0 };
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			scanf("%d", &ori[i][j]);
			a[i][j] = ori[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			printf("%d", a[i][j]);
		}
	}
	int e, b, c, d;
	scanf("%d %d %d %d", &e, &b, &c, &d);
	int res = a[e][b] + a[c - 1][d - 1] - a[e][d - 1] - a[c - 1][b];
	return 0;
}

这一段代码可以打印出来二维数组的前缀和,并且可以打印出从e,b到c,d之间的和

要是理解好了二维数组的前缀和,那么理解二维数组的差分也是必要的,这也能帮我们优化代码

直接先上一个我自己写的,并且觉得数组个数最少的一个二维差分代码

#define max(a,b) (((a)>(b))?(a):(b))
#include <stdio.h>
#include <string.h>
int arr[1005][1005] = { 0 };
int s[1005][1005] = { 0 };
int main()
{
	int n, m;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			scanf("%d", &arr[i][j]);
			s[i][j] = arr[i][j] - arr[i - 1][j] - arr[i][j - 1] + arr[i - 1][j - 1];
		}
	}
	for (int i = 1; i <= m; i++)
	{
		int x1, x2, y1, y2;
		scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
		s[x1][y1] += 1;
		s[x1][y2 + 1] -= 1;
		s[x2 + 1][y1] -= 1;
		s[x2 + 1][y2 + 1] += 1;
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			printf("%d ", s[i][j]);
		}
		printf("\n");
	}
	return 0;
}

这里的arr为输入的初始代码,s是表示差分后的代码

这一段代码是在输入arr的同时利用相邻的差值来记录s数组应该的大小

接下来这一段代码则是限定次数的设置差分的权值和改变的位置

接下来就直接利用二维数组的前缀和解决问题,记住,差分加前缀和的实质就是利用数组中的元素相互之间的差值来记录数组中元素的大小

最后我们再把差分后的二维数组全部打印出来观察是不是差分正确

熟练利用差分可以减小做题中的时间复杂度,而这也是最基础的算法之一

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值