【小米邀请赛一:二维差分(裸题)】J :Matrix Subtraction

二维差分

难题

3 / 10 3/10 3/10
裸题经典题

题意

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

数据范围

样例组数 T ≤ 100 T\le 100 T100
n , m ≤ 1000 n,m\le 1000 n,m1000
a , b ≤ 1000 a,b\le 1000 a,b1000

思路

对于最最上角的非零元素 M i , j M_{i,j} Mi,j,一定是以它为左上角的 ( a , b ) (a,b) a,b 的矩阵
矩阵中每个元素都减去 M i , j M_{i,j} Mi,j

非法情况:如果遇到无法选择的矩阵(以它为左上角无法选出 ( a , b ) (a,b) (a,b)的矩阵的情况)或者元素减掉之后变为负数了,都是无解。

但是光遍历矩阵元素就已经 O ( n m ) O(nm) O(nm) 了,很明显无法直接遍历的方法来让选出的 ( a , b ) (a,b) (a,b)矩阵的元素减少。

这时候,我们想到一维差分,但是明显会 O ( n 2 m ) O(n^2m) O(n2m)明显也会超时。

这时候,我们想到二维差分,明显就 O ( n m ) O(nm) O(nm) 了,但是不大会。

【正题】

对于一个二维差分数组 d e [ i ] [ j ] de[i][j] de[i][j],如果要对左上角坐标为 ( x , y ) (x,y) (x,y)
矩阵大小为 a , b a,b a,b
右下角坐标为 ( x + a − 1 , y + b − 1 ) (x+a-1,y+b-1) (x+a1,y+b1) 的矩阵的元素同时增加 c c c
那么代码如下:

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;
}

然后差分数组,获取 ( i , j ) (i,j) (i,j) 的元素值:

   de[i][j] = de[i][j] + de[i][j-1] + de[i-1][j] - de[i-1][j-1];

为什么画个图就知道了->
在这里插入图片描述
由于循环从左上到右下遍历,故差分数组表示该点到右下角的矩阵元素和
红色区域加C,绿色蓝色区域减C,黄色区域加C就等价于中间区间加C

核心代码

时间复杂度 O ( n m ) O(nm) O(nm)
空间复杂度 O ( n m ) O(nm) O(nm)

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值