1314. 矩阵区域和 类似dp的思想减少计算量

二维前缀和思路

leetcode官方题解

二维前缀和代码

class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int K) {
        int k=K;
        int m=mat.size(),n=mat[0].size();
        vector<vector<int>>ssum(m+1,vector<int>(n+1));
        // ssum[i][j]代表以[i-1][j-1]为右下角的矩阵的二维和
        vector<vector<int>>ans(m,vector<int>(n));
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                ssum[i][j]=ssum[i-1][j]+ssum[i][j-1]-ssum[i-1][j-1]+mat[i-1][j-1];
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                ans[i][j]=ssum[min(i+k+1,m)][min(j+k+1,n)]-ssum[max(i-k,0)][min(j+k+1,n)]-ssum[min(i+k+1,m)][max(j-k,0)]+ssum[max(i-k,0)][max(j-k,0)];
            }
        }
        return ans;
    }
};

一维前缀和思路

  1. 自己用的方法不是 d p dp dp,套个dp的壳子。
  2. d p [ i ] [ j ] dp[i][j] dp[i][j]代表 ( r , c ) (r,c) (r,c) ( i , j ) (i,j) (i,j)的时候我们得到的这个小矩形的和。
  3. d p [ i ] [ j ] = d p [ i ] [ j + 1 ] + 新 增 的 最 左 边 的 那 一 列 的 和 − 失 去 的 最 右 边 那 一 列 的 和 = d p [ i ] [ j + 1 ] + ∑ a = i − k a = i + k m a t [ a ] [ j − k ] − ∑ a = i − k a = i + k m a t [ a ] [ j + k + 1 ] dp[i][j]=dp[i][j+1]+新增的最左边的那一列的和-失去的最右边那一列的和=dp[i][j+1]+\sum_{a=i-k}^{a=i+k}mat[a][j-k]-\sum_{a=i-k}^{a=i+k}mat[a][j+k+1] dp[i][j]=dp[i][j+1]+=dp[i][j+1]+a=ika=i+kmat[a][jk]a=ika=i+kmat[a][j+k+1]
  4. d p [ i ] [ j ] = d p [ i + 1 ] [ j ] + 新 增 的 最 上 边 的 那 一 行 的 和 − 失 去 的 最 下 边 那 一 行 的 和 = d p [ i + 1 ] [ j ] + ∑ a = j − k a = j + k m a t [ i − k ] [ a ] − ∑ a = j − k a = j + k m a t [ i + k + 1 ] [ a ] dp[i][j]=dp[i+1][j]+新增的最上边的那一行的和-失去的最下边那一行的和=dp[i+1][j]+\sum_{a=j-k}^{a=j+k}mat[i-k][a]-\sum_{a=j-k}^{a=j+k}mat[i+k+1][a] dp[i][j]=dp[i+1][j]+=dp[i+1][j]+a=jka=j+kmat[ik][a]a=jka=j+kmat[i+k+1][a]
  5. 初始状态 d p [ m − 1 ] [ ] n − 1 ] dp[m-1][]n-1] dp[m1][]n1]计算出来。
  6. 我们从右下角一行一行的更新到左上角,那么我们的每一行只需要计算好最右边的一个元素,那一行的元素就都可以计算了,采用3. 中描述的表达式,当更新新的一行的时候,需要用到4.的表达式计算出来第一个元素
  7. 需要特别注意上面的 [ ] [] []里面的变量的范围,涉及到边界的要进行特殊的处理。
  8. 为了优化计算速度,设 s s u m [ ] [ ] , s 1 s u m [ ] [ ] ssum[][],s1sum[][] ssum[][],s1sum[][]分别为一行一行的前缀和、一列一列的前缀和,这样我们就可以简化 ∑ a = i − k a = i + k m a t [ a ] [ j − k ] \sum_{a=i-k}^{a=i+k}mat[a][j-k] a=ika=i+kmat[a][jk]类似于这种式子的计算。
  9. 时间复杂度 O ( n 2 ) O(n^2) O(n2),大概是这样吧。

一维前缀和代码

class Solution {
public:
    vector<vector<int>>ssum, s1sum;
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int K) {
        int k = K;
        int m = mat.size();
        int n = mat[0].size();
        if (m == 0 || n == 0) {
            return vector<vector<int>>();
        }
        ssum.resize(m);
        s1sum.resize(m);

        for (int i = 0; i < m; i++) {
            ssum[i].resize(n);
            s1sum[i].resize(n);
            ssum[i][0] = mat[i][0];
        }
        for (int j = 0; j < n; j++) {
            s1sum[0][j] = mat[0][j];
        }
        for (int j = 1; j < n; j++) {
            for (int i = 0; i < m; i++) {
                ssum[i][j] = ssum[i][j - 1] + mat[i][j];
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 0; j < n; j++) {
                s1sum[i][j] = s1sum[i - 1][j] + mat[i][j];
            }
        }
        // 至此,两个前缀和计算完毕

        vector<vector<int>>dp(m);
        for (int i = 0; i < m; i++) {
            dp[i].resize(n);
        }
		
        // 下面进行初始状态dp[m-1][n-1]的计算。
        dp[m - 1][n - 1] = 0;
        for (int i = max(m - 1 - k, 0); i < m; i++) {
            if (n - 2 - k < 0) {
                dp[m - 1][n - 1] += ssum[i][n - 1] - ssum[i][0];
                dp[m - 1][n - 1] += mat[i][0];
            }
            else {
                dp[m - 1][n - 1] += ssum[i][n - 1] - ssum[i][n - 2 - k];
            }
        }
		
        // 从右下角开始,一行一行的计算dp[i][j]
        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                if (i == m - 1 && j == n - 1) {
                    continue;
                }
                // 最右边的
                if (j == n - 1) {
                    dp[i][j] = dp[i + 1][j];
                    if (i - k >= 0) {
                        if (j - k - 1 < 0) {
                            dp[i][j] += ssum[i - k][min(j + k, n - 1)] - ssum[i - k][0];
                            dp[i][j] += mat[i - k][0];
                        }
                        else {
                            dp[i][j] += ssum[i - k][min(j + k, n - 1)] - ssum[i - k][j - k - 1];
                        }
                    }
                    if (i + k + 1 < m) {
                        if (j - k - 1 < 0) {
                            dp[i][j] += -(ssum[i + k + 1][min(j + k, n - 1)] - ssum[i + k + 1][0]);
                            dp[i][j] += -(mat[i + k + 1][0]);
                        }
                        else {
                            dp[i][j] += -(ssum[i + k + 1][min(j + k, n - 1)] - ssum[i + k + 1][j - k - 1]);
                        }
                    }
                }
                // 非最右边的
                else {
                    dp[i][j] = dp[i][j + 1];
                    if (j - k >= 0) {
                        if (i - k - 1 < 0) {
                            dp[i][j] += s1sum[min(i + k, m - 1)][j - k] - s1sum[0][j - k];
                            dp[i][j] += mat[0][j - k];
                        }
                        else {
                            dp[i][j] += s1sum[min(i + k, m - 1)][j - k] - s1sum[i - k - 1][j - k];
                        }
                    }
                    if (j + k + 1 < n) {
                        if (i - k - 1 < 0) {
                            dp[i][j] += -(s1sum[min(i + k, m - 1)][j + k + 1] - s1sum[0][j + k + 1]);
                            dp[i][j] += -(mat[0][j + k + 1]);
                        }
                        else {
                            dp[i][j] += -(s1sum[min(i + k, m - 1)][j + k + 1] - s1sum[i - k - 1][j + k + 1]);
                        }
                    }
                }
            }
        }
        return dp;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值