【蓝桥杯】统计子矩阵【C++】

在这里插入图片描述

暴力题解

乍一看题目的解体方法是,二维前缀和+二分查找,但是这样的话只能拿到70%的分

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1000000007;
const int N = 505;

ll m, n, k;
ll a[N][N];
ll ans = 0;

ll getsum(int i, int j, int x, int y) {
    
    
	return a[x][y] - a[i - 1][y] - a[x][j - 1] + a[i - 1][j - 1];
}

int main() {
    
    
	cin >> m >> n >> k;
	for (int i = 1; i <= m; i++) {
    
    
		for (int j = 1; j <= n; j++) {
    
    
			cin >> a[i][j];
			a[i][j] += a[i - 1][j] - a[i - 1][j - 1] + a[i][j - 1];
		}
	}
	for (int i = 1; i <= m; i++) {
    
    
		for (int j = 1; j <= n; j++) {
    
    
			if (getsum(i, j, i, j) > k) continue;
			for (int x = i; x <= m; x++) {
    
    
				int l = j, r = n;
				while (l < r) {
    
    
					int mid = (l + r + 1) >> 1;
					if (getsum(i, j, x, mid) > k) r = mid - 1;
					else l = mid;
				}
				if (getsum(i, j, x, l) > k) break;
				ans += l - j + 1;
			}
		}
	}
	cout << ans << endl;
	return 0;
}


真正题解(一维前缀+双指针滑动窗口)

我们枚举上边界和下边界,即当前枚举矩阵是处在第x行(上边界),和y(下边界)之间的。接着枚举矩形的右端点,我们可以发现,每次当右端点向右移动的时候,矩形的面积是单调递增的,此时如果矩形的面积大于K,则不合要求,固定右端点,此时我们将当前矩形的左端点也向右移动,矩形的面积单调变小,直到举行面积再次小于K,停止移动。

那么我们怎么才能得到子矩阵的数目呢?
其实就是右边界到达临界值的时候,减去左边界再加1,得到的数就是我们循环一次能得到的子矩阵的数量。

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int maxn=505;
int g[maxn][maxn];//原数组
int sum[maxn][maxn];//前缀和数组

int main()
{
    
    
	ios::sync_with_stdio(false) ;//这一行代码的作用是给cin和cout加速 

	cin>>n>>m>>k;
	for(int i = 1; i <= n ; i++)
	{
    
    
		for(int j = 1; j <= m ; j++)
		{
    
    
			cin>>g[i][j];
			// 按列的前缀和放入sum数组中
			sum[i][j] = g[i][j] + sum[i-1][j];
		}
	}
//  验证sum数组中的数是不是按列的前缀和
//	for(int i = 1; i <= n ; i++)
//	{
    
    
//		for(int j = 1; j <= m ; j++)
//		{
    
    
//			cout<<sum[i][j]<<' ';
//			
//		}
//		cout<<endl;
//	}
	
	long long m_count = 0;
	//把和想象成四个点围成的矩形的面积
	for(int x = 1; x <= n; x++)//x是上边界
	{
    
    
		for(int y = x; y <= n; y++)//y是下边界
		{
    
    
			for(int r = 1, l = 1, s = 0; r <= m; r++)//r是右边界,l是左边界
			{
    
    
				
				s += sum[y][r] - sum[x - 1][r];
				while(s > k && l < r)
				{
    
    
					s = s - sum[y][l] + sum[x - 1][l];
					l++;
				}
				if(s <= k){
    
    
					m_count += r - l + 1;
				}
			}
		}
	}
	
	cout << m_count << endl;

	return 0;
	
}

总结

在做这一题之前最好去学习一下一维前缀和二维前缀的算法,搞清楚前缀和是怎么得出来的,在纸上写写画画,不然很难理解。

猜你喜欢

转载自blog.csdn.net/qq_63524016/article/details/129216155