蓝桥杯22年第十三届省赛-统计子矩阵|一维前缀和加双指针

题目链接:

1.统计子矩阵 - 蓝桥云课 (lanqiao.cn)

蓝桥杯2022年第十三届省赛真题-统计子矩阵 - C语言网 (dotcpp.com)

说明:

涉及到子矩阵的时候,一般就跟前缀和相关,可以降维。

先回顾一下最大子矩阵,回忆一下一维前缀和的模板,枚举列(行也可以,计算前缀和时就算列上的前缀和)的起点和终点,求每行上的元素之和的时候时间优化为O(1):

最大子段和和最大子矩阵|动态规划-CSDN博客

如果列的起点和终点确定,那么求小于k的子矩阵个数其实就是求第1行到第m行有多少个和小于k的连续子序列。

用l,r表示这个子序列的左右端点,

如果

因为矩阵元素都为正数

1.sum(l,r)<k,那么sum(i,r)<k ; l<=i<=r合法的新子序列个数就是r-l+1;

2.sum(l,r)>k,那么sum(l,j)>k;r<=j<=m, 当前区间已经大于k了,没必要再继续向下挪动r了,那就移动l

再注意一下自己的错误:

cpp 复制代码
//错误代码,不能加这个判断 ,因为,当t和b都指向一个元素时,这个元素还大于k
			//s减为0,t移动到b+1位置,并且接下来仍有可能出现小于k的矩阵 	
			//if(t>b) break;
			
			/*错误代码,因为此时小于k的矩阵不止一个,而是 b-t+1个
			即i+1列到j列上,t行到b行的矩阵和小于k,那么,固定t, t到b-1行也小于k
			t到b-2行也小于k,t到X(t<=X<=b)行都小于k,应该加上t到b的数字个数
			反过来也一样,b移动到下个位置小于k,将b固定,b行到t行 也是 b-t+1个
			if(s<=k) {
				ans++;
			}*/

总结一下:

  1. 子矩阵问题可能会用前缀和降维
  2. 求小于某个值的连续子序列的数量时,使用双指针。l和r都起始点开始,移动r指针,序列和小于k时,新答案有r-l+1个;大于k时,移动l指针直到小于k。

代码:

只用前缀和的版本,两层循环控制列的起点和终点,两层循环控制行的起点和终点。会超时

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N =500+10;
int ans = 0;
int k;

int mx[N][N]={0};
signed main() {
 
    ios::sync_with_stdio(0); 
    cin.tie(0);
    cout.tie(0);
   
    int m,n;
    cin>>m>>n;
    cin>>k;
    
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>mx[i][j];
            mx[i][j]+=mx[i][j-1];
        }
    }
    
    for(int i=0;i<=n-1;i++){
        for(int j=i+1;j<=n;j++){
        
        for(int t=1;t<=m;t++){
          int s=0;
           for(int b=t;b<=m;b++){
               s+=mx[b][j]-mx[b][i];
               if(s<=k) 
               ans++;
            else break;

           }
        }
        
      }
    }
   
    
  cout<<ans;
  return 0;
}

加双指针:

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N =500+10;
int ans = 0;
int k;

int mx[N][N]={0};
signed main() {
 
    ios::sync_with_stdio(0); 
    cin.tie(0);
    cout.tie(0);
   
    int m,n;
    cin>>m>>n;
    cin>>k;
    
    //计算每行上的前缀和 
    for(int i=1;i<=m;i++){
    	for(int j=1;j<=n;j++){
    		cin>>mx[i][j];
    		mx[i][j]+=mx[i][j-1];
		}
	}
	
//	for(int i=0;i<=n-1;i++){
//		for(int j=i+1;j<=n;j++){
//		
//      此处 要控制起始行和终点行 
//		for(int t=1;t<=m;t++){
//		  int s=0;
//		   for(int b=t;b<=m;b++){
//		   	s+=mx[b][j]-mx[b][i];
//		   	if(s<=k) 
//		   	ans++;
//		    else break;
//		   }
//		}
//		
//	  }
//	}
   
    	for(int i=0;i<=n-1;i++){
		for(int j=i+1;j<=n;j++){
		
		//t头指针,b尾指针
		//当和大于k,头指针向右挪动
        //尾指针最后都要移动
		for(int t=1,b=1,s=0;t<=m&&b<=m;b++){
			//int s=0; 应该放在上面初始化为0 
		  
		   s+=mx[b][j]-mx[b][i];
		   	
		   	while(s>k){
		    		s-=mx[t][j]-mx[t][i];
		    		t++;
				} 
			//错误代码,不能加这个判断 ,因为,当t和b都指向一个元素时,这个元素还大于k
			//s减为0,t移动到b+1位置,并且接下来仍有可能出现小于k的矩阵 	
			//if(t>b) break;
			
			/*错误代码,因为此时小于k的矩阵不止一个,而是 b-t+1个
			即i+1列到j列上,t行到b行的矩阵和小于k,那么,固定t, t到b-1行也小于k
			t到b-2行也小于k,t到X(t<=X<=b)行都小于k,应该加上t到b的数字个数
			反过来也一样,b移动到下个位置小于k,将b固定,b行到t行 也是 b-t+1个
			if(s<=k) {
				ans++;
			}*/
			ans+=b-t+1;
	}
	  }
	}
  cout<<ans;
  return 0;
}
相关推荐
mingzhi612 小时前
网安面试会问到的:http的长连接和短连接
http·面试·职场和发展
huanxiangcoco5 小时前
73. 矩阵置零
python·leetcode·矩阵
luluvx5 小时前
LeetCode[中等] 74.搜索二维矩阵
算法·leetcode·矩阵
辰哥单片机设计7 小时前
4×4矩阵键盘详解(STM32)
stm32·单片机·嵌入式硬件·矩阵·计算机外设·传感器
正义的彬彬侠9 小时前
举例说明计算一个矩阵的秩的完整步骤
人工智能·机器学习·矩阵·回归
新榜有数15 小时前
品牌建设是什么?怎么做好品牌建设?
大数据·矩阵·数据分析·新媒体运营·流量运营·媒体·内容运营
Pandaconda16 小时前
【计算机网络 - 基础问题】每日 3 题(十)
开发语言·经验分享·笔记·后端·计算机网络·面试·职场和发展
测试老哥18 小时前
功能测试干了三年,快要废了。。。
自动化测试·软件测试·python·功能测试·面试·职场和发展·压力测试
咸鱼菲菲19 小时前
同态加密明文矩阵乘密文向量优化:BSGS小步大步法
安全·矩阵·同态加密