二维差分(讲解)

转载于:https://blog.csdn.net/justidle/article/details/104506724

什么是二维差分

我们有一个矩阵,如下图所示。

根据二维前缀和表示的是右上角矩形的和,由于差分只涉及前面相邻的数(由一维可以推出),并且由前面范围的数相加得到这个位置的数。那么类比二维前缀和和一维差分,可以简单推测出二维差分的公式

p[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]

如何从差分矩阵得到原矩阵呢?可以参考下面公式

a[i][j] = p[i][j]+p[i-1][j]+p[i][j-1]-p[i-1][j-1]

举例

比如,我们有一个矩阵 a,如下所示

 1 2 4 3

 5 1 2 4

 6 3 5 9

那么对应的二维差分矩阵 p 如下: 

1 1 2 -1 

4 -5 -1 3 

1 1 1 2

应用

 如果我们要在左上角是 (x1,y1),右下角是 (x2,y2) 的矩形区间每个值都 +a,如下图所示

在我们要的区间开始位置(x1,y1)处 +c,根据前缀和的性质,那么它影响的就是整个黄色部分,多影响了两个蓝色部分,所以在两个蓝色部分 -c 消除 +c 的影响,而两个蓝色部分重叠的绿色部分多了个 -c 的影响,所以绿色部分 +c 消除影响。所以对应的计算方法如下: 

diff[x1][y1] += c; 

diff[x1][y2+1] -=c; 

diff[x2+1][y1] -=c; 

diff[x2+1][y2+1] += c;

模板题:https://ac.nowcoder.com/acm/contest/7501/J

题意

给你一个 n,m 的矩阵。
你每次可以选择一个a,b 的矩阵,每个元素都同时减去1。
问你经过一些操作之后,能否得到全0的矩阵。

数据范围

样例组数 T ≤ 100
n , m ≤ 1000
a , b ≤ 1000

代码:

///难理解版本:
const int MAX = 1e3+50;
ll aa[MAX][MAX];
ll de[MAX][MAX];
void ins(int x,int y,ll c,int a,int b){
    int dx = x + a;
    int dy = y + b;
    de[x][y] -= c;
    de[x][dy] += c;
    de[dx][y] += c;
    de[dx][dy] -= c;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m,a,b;
        scanf("%d%d%d%d",&n,&m,&a,&b);
        for(int i = 0;i <= n+1;++i)
        for(int j = 0;j <= m+1;++j)
            de[i][j] = 0;
        for(int i = 1;i <= n;++i)
        for(int j = 1;j <= m;++j){
            scanf("%d",&aa[i][j]);
            ins(i,j,-aa[i][j],1,1);        /// 负号意味着加上
        }

        bool flag = true;
        for(int i = 1;flag && i <= n;++i){
            for(int j = 1;flag && j <= m;++j){
                de[i][j] = de[i][j] + de[i][j-1] + de[i-1][j] - de[i-1][j-1];
                if(de[i][j] == 0)continue;
                if(de[i][j] < 0)flag = false;
                if(de[i][j] > 0){
                    if(i + a - 1 > n || j + b - 1 > m)flag = false;
                    else{
                        ins(i,j,de[i][j],a,b);    /// 正号意味着减去
                    }
                }
            }
        }
        if(flag)puts("^_^");
        else puts("QAQ");
    }
    return 0;
}
#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int MMAX=1e3+5;
int mp[MMAX][MMAX],re[MMAX][MMAX];
int n,m,a,b,t,res[MMAX][MMAX];
/// mp[][]存初始矩阵,re[][]存前缀差分矩阵,res[][]存最后遍历得到的终极差分矩阵
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&n,&m,&a,&b);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&mp[i][j]);
                re[i][j]=mp[i][j]-mp[i-1][j]-mp[i][j-1]+mp[i-1][j-1]; ///初始化前缀差分矩阵re[][]
            }
        }
        int f=0; ///定义标记变量,为0代表可以,为1代表不可以
        for(int i=1;i<=n-a+1;i++){
            for(int j=1;j<=m-b+1;j++){
                res[i][j]=re[i][j]+res[i-1][j]+res[i][j-1]-res[i-1][j-1]; ///计算更新过的ij位置的值
                if(res[i][j]<0) f=1; ///如果小于0代表不符合要求
                if(f==1) break; 
                if(res[i][j]==0) continue;
                ///如果大于0代表需要减1操作res[i][j]次
                re[i][j]-=res[i][j];
                re[i+a][j]+=res[i][j];
                re[i][j+b]+=res[i][j];
                re[i+a][j+b]-=res[i][j];
                ///上述四步是核心代码,即更新区间[i][j]到[i+a][j+b]区间的值
                res[i][j]=0;
            }
            if(f==1) break;
        }
        if(f==1) printf("QAQ\n"); ///如果不符合直接输出QAQ
        else{
            for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                        ///遍历寻找是否全部满足
                    res[i][j]=re[i][j]+res[i-1][j]+res[i][j-1]-res[i-1][j-1];
                    if(res[i][j]!=0) f=1;
                    if(f==1) break;
                }
                if(f==1) break;
            }
            if(f==1) printf("QAQ\n");
            else printf("^_^\n");
        }
    }
    return 0;
}

 

posted @ 2020-10-28 21:19  小垃圾的日常  阅读(2067)  评论(0)    收藏  举报