差分约束

题目

给一个m * n的矩阵,现在每次可以选择以元素ai为第一行第一列的大小为a * b的矩阵,矩阵中的每个元素都将-ai。选取的矩阵不能超过m*n的范围之外。现在问是否能将矩阵中的元素全部变为0.

解法

由于选取的a * b矩阵必须在原矩阵之中,那么第1行,第1列的元素肯定是要作为起始元素的。因此,我们从上到下,从左到右依此进行消除,看最终能否消除成功。时间复杂度为O(n * m * a * b)。
上面的算法m * n是不可避免的 ,我们可以用二维差分来优化区间修改操作。原始算法我们需要O(a * b)的时间复杂度进行修改,现在我们可以只用O(1)的时间复杂度进行修改。
二维差分
diff[i][j]=diff[i][j]-diff[i-1][j]-diff[i][j-1]+diff[i-1][j-1]
修改时:若对矩形(x1,y1,x2,y2)进行区间加tmp,则等效于mp[x1][y1]+=tmp ; mp[x2+1][y2+1]+=tmp ; mp[x1][y2+1]-=tmp; mp[x2+1][y1]-=tmp
代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
int mp[1010][1010];
int diff[1010][1010];
int main()
{
    int T,n,m,a,b;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++){
       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]);
                diff[i][j]=mp[i][j]-mp[i-1][j]-mp[i][j-1]+mp[i-1][j-1];
            }  
        }
        bool flag=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                int tmp=diff[i][j];
                //cout<<tmp<<endl;
                if(!tmp)  continue;
                else if(tmp<0)
                {
                    flag=0;
                    break;
                }
                else if(i+a-1<=n&&j+b-1<=m)
                {
                    diff[i][j]-=tmp;
                    diff[i+a][j+b]-=tmp;
                    diff[i][j+b]+=tmp;
                    diff[i+a][j]+=tmp;
                }
                else{
                    flag=0;
                    break;
                }
            }
        }
        if(!flag)
            cout<<"QAQ"<<endl;
        else
            cout<<"^_^"<<endl;
    }
}
posted @ 2020-11-09 14:46  blueattack  阅读(83)  评论(0编辑  收藏  举报