差分(一维+二维)

在上篇文章当中讲述了前缀和的有关知识,这期给大家带来差分的知识。

首先什么是差分?

给一个原数组a[],再给定一个差分数组g[],那么原数组与差分数组的关系:

g[1] = a[1];  g[2] = a[2] - a[1];  g[3] = a[3] - a[2].....

那么我们通过对其关系可以推出:

g[1] + g[2] + g[3] = a[1] + (a[2] - a[1]) + (a[3] - a[2]) = a[3]

根据上面的等量关系,我们不难看出对差分数组前i个元素进行前缀和就会得到原数组a[i]的值

那么直到这个性质后,我们再来研究差分数组有什么用处呢?

在讲述前缀和的作用,我们知道了前缀和可以快速求出某一段元素所对应的原数组的和,而差分可以快速的对某一段元素分别加或减值

下面给出的是一维差分数组的计算方式:

#include <iostream>
using namespace std;
const int N = 1000;
int a[N],g[N];
void insert(int l,int r,int c)
{
  g[l] += c;
  g[r + 1] -= c;
}
int main()
{
  int n;
  for(int i = 0;i <= n;i++)
  {
    cin >> a[i];
    insert(i,i,a[i]);//计算差分并赋值
  }
  for(int i = 0;i <= n;i++)  
    cout << g[i] << ' ';//打印差分数组
  return 0;
}

如果要是想在某段区间内每个元素对应的值+c:

#include <iostream>
using namespace std;
const int N = 1000;
int a[N],g[N];
int s[N];//记录差分数组前缀和后的数组
void insert(int l,int r,int c)
{
  g[l] += c;
  g[r + 1] -= c;
}
int main()
{
  int n;
  for(int i = 1;i <= n;i++)
  {
    cin >> a[i];
    insert(i,i,a[i]);//计算差分数组
  }
  int m;
  cin >> m;//输入更改次数
  while(m--)
  {
    int l,r,c;//在l - r范围内的元素+c
    cin >> l >> r >> c;
    insert(l,r,c);
  }
  for(int i = 1;i <= n;i++)
    s[i] = s[i - 1] + g[i];//用动态规划对差分数组前缀和将改完后的原数组的值存入s[]
  for(int i = 1;i <= n;i++)  
    cout << s[i] << ' ';//输出改完后的原数组
  return 0;
}

对差分数组进行前缀和就可以得到原数组,那么接下来看一下二维差分数组:

首先其二维与前缀和的二维大概理解差不多,只不过其面积要理解成当前(i,j)点与右下角的点形成的矩形面积,对于二维差分矩阵:

g[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1]    (这个可以理解为前缀和反向计算原数组的含义)

要是对于二维原矩阵进行区间修改:

void insert(int x1,int x2,int y1,int y2,int c)
{
  g[x1][y1] += c;
  g[x2 + 1][y1] -= c;
  g[x1][y2 + 1] -= c;
  g[x2 + 1][y2 + 1] += c;
}

因为g数组是a数组的差分数组,a数组是g数组的前缀和数组,所以g数组在a数组上代表的面积与前缀和代表的面积相反,是与右下角的点构成的矩形面积,所以通过面积作差得到原数组的值。

而其是以(x1,y1)为起点,在这里+c代表其整个矩阵内的所有元素分别+c,这样理解的话,为了让(x1,y1)与(x2,y2)构成的矩阵内的值+c就需要后面的三行进行调整(面积的割补)

#include <iostream>
using namespace std;
const int N = 1000;
int a[N][N];  //原数组
int g[N][N];  //差分数组
void insert(int x1,int x2,int y1,int y2,int c)
{
	g[x1][y1] += c;
	g[x2 + 1][y1] -= c;
	g[x1][y2 + 1] -= c;
	g[x2 + 1][y2 + 1] += c;
}
int main()
{
	int n,m,s;
	cin >> n >> m >> s;
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			cin >> a[i][j];
			insert(i,i,j,j,a[i][j]);//计算差分并赋值
		}
	}
    更改s次,在(x1,y1)与(x2,y2)构成的矩阵内的值+c	
	while(s--)
	{
		int x1,y1,x2,y2,c;
		cin >> x1 >> x2 >> y1 >> y2 >> c;
		insert(x1,x1,y1,y2,c);
	}
    //利用动态规划进行前缀和
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			g[i][j] += g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1];//差分矩阵前缀和(DP)
		}
	}
    //打印
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= m;j++)
		{
			cout << g[i][j] << ' ';//打印更改后的原数组
		}
        cout << endl;
	}
	return 0;
}

到这里不难发现求前缀和就是利用动态规划,而差分数组的赋值以及对原数组加减值是利用上面构造的insert函数,差分数组前缀和可得到原数组,前缀和数组进行差分同样也是可以得到原数组的。 

好了,本期内容就到这里了,后序会继续更新算法的相关知识。

感谢收看,记得三连支持一下。

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记得开心一点嘛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值