差分法(C++)

一维的差分序列

题目:https://www.acwing.com/activity/content/problem/content/831/

首先暴力解法就是每次询问的时候进行循环遍历进行相加。原理很简单,可以直接看代码。

暴力解法: 

#include<iostream>
using namespace std;

const int N=1e5+10;
int n,m;
int a[N];


int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    while(m--)
    {
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        cin>>l>>r>>c;
        for(int i=l;i<=r;i++)
        {
            a[i]+=c;
        }
        
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d ",a[i]);
    }
    return 0;
}

为了降低时间复杂度,可以采用差分法来解决。

构造两个整数数组a,b来分别存储原整数序列,存储原整数序列的差分项。

这句话有点难以理解,意思就是a里面存放的是正常题目给的东西,而b存放的是a的差分,a也就成为了b的前缀和。

举个例子方便理解:

a[1]=b[1]

a[2]=b[1]+b[2]

......

a[n]=b[1]+b[2]+...b[n]

也就是说b[n]=a[n]-a[n-1],就是利用这个公式来初始化b数组的。

接下来细说具体是如何使用a,b来解决具体问题的

题目要求在(L、R)这个区间中的数字都加上整数C,那我们只需要给b[L]加上C这样会使得a[L]~a[n]中的数都加上C,因为我们要是的是区间中的加C,所以还要减去多加的a[R]后面的!也就是给b[R+1]减去C。

利用差分法解决:

#include<iostream>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],b[N];

void insert(int l,int r,int c)
{
    b[l]+=c;
    b[r+1]-=c;
}


int main()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++) scanf("%d",&a[i]);
   //初始化b
   for(int i=1;i<=n;i++)  insert(i,i,a[i]);
   
   while(m--)
   {
       int l,r,c;
       scanf("%d%d%d",&l,&r,&c);
       insert(l,r,c);
   }
   //已经通过b来完成了加减操作
   //将b变成其前缀和数组即可
   for(int i=1;i<=n;i++) b[i]+=b[i-1];
   
   for(int i=1;i<=n;i++) printf("%d ",b[i]);
   return 0;
}

接下来是二维的差分矩阵,先看题目

https://www.acwing.com/activity/content/problem/content/832/

解决方法比较类似之前二维前缀和矩阵的解决办法https://blog.csdn.net/qq_63055790/article/details/135567640

也是定义两个整数矩阵a和b,b矩阵是存放差分矩阵的。 

然后类似于一维的,控制差分矩阵b来控制对于a与c的相加即可,具体如图所示

b[x1][y1]+=c; b[x2+1][y1]-=c; b[x1][y2+1]-=c;b[x2+1][y2+1]+=c;

减去相应多加的c即可,道理都一样。 

 最后将b转为前缀和矩阵的时候与之前前缀和矩阵初始化是一样的。

#include<iostream>
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],b[N][N];

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

int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&a[i][j]);
    
    
    //初始化差分矩阵b
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    insert(i,j,i,j,a[i][j]);
    
    while(q--)
    {
        int x1,y1,x2,y2,c;
        cin>>x1>>y1>>x2>>y2>>c;
        insert(x1,y1,x2,y2,c);
    }
    
    //将b矩阵转为前缀和矩阵输出
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
    
    for(int i=1;i<=n;i++)
    {
    for(int j=1;j<=m;j++)
    cout<<b[i][j]<<" ";
    cout<<endl;
    }
    
    return 0;
}

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

松定

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

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

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

打赏作者

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

抵扣说明:

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

余额充值