CF1731D题解

题目传送门
题目大意:


本题翻译的让很多蒟蒻十分懵逼,我在这里再翻译一下:

一共有t组数据,每组数据都有一个n和m,表示长和宽,再输入一个二维矩阵,之后,你需要选取一个边长为l的正方形,使这里面所有数都大于等于l,求l的最大值。

 ( 1 <= n, m1≤n≤m ; 1<=n*m≤10^6 )

思路:


本题的数组定义就难倒了很多蒟蒻。因为n*m小于等于1000000,如果都定1000000的话,那么就会MLE,这里有三种解决方案:

1.定一维数组,用的时候在导成二维

2.用vector

3.在C++的高版本中,我们可以定义a[n][m],每次用的时候清空,本蒟蒻用的就是这种。

int a[n+1][m+1];
int z[n+1][m+1];
int b[n+1][m+1];
for(int i=0;i<=n;i++){
    for(int j=0;j<=m;j++){
        z[i][j]=0;
        b[i][j]=0;
    }
}
for(int i=0;i<=m;i++){
    a[0][i]=0;
}for(int i=1;i<=n;i++){
    a[i][0]=0;
}

如果我们一个一个找l肯定会超时,所以我们要使用二分。

那么怎么判断是否合法呢?

我们定义了数组 b和z,我们可以用b来表示某个数是否大于等于l,如果是,那么b[i][j]就为0,否则为1。z是b的前缀和。我们用ans表示答案,初始值为一个极大值,每次和一个区间的z取min,最后看看是不是0就可以了。

代码:

#include<iostream>
#define int long long
using namespace std;
int n,m,k;
signed main(){
	cin>>k;
	while(k--){
		cin>>n>>m;
		int a[n+1][m+1];
		int z[n+1][m+1];
		int b[n+1][m+1];
		for(int i=0;i<=n;i++){
			for(int j=0;j<=m;j++){
				z[i][j]=0;
				b[i][j]=0;
			}
		}
		for(int i=0;i<=m;i++){
			a[0][i]=0;
		}for(int i=1;i<=n;i++){
			a[i][0]=0;
		}for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>a[i][j];
			}
		}int l=1,r=min(n,m),mid;
		while(l<=r){
			mid=(l+r)/2;
			int sum=2147483647;
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					if(a[i][j]>=mid){
						b[i][j]=0;
					}else{
						b[i][j]=1;
					}z[i][j]=z[i-1][j]+z[i][j-1]-z[i-1][j-1]+b[i][j];
				}
			}
			for(int i=1;i<=n-mid+1;i++){
				for(int j=1;j<=m-mid+1;j++){
					int li=i+mid-1;
					int lj=j+mid-1;
					sum=min(sum,z[li][lj]-z[i-1][lj]-z[li][j-1]+z[i-1][j-1]);
				}
			}
			if(sum==0){
				l=mid+1;
			}else{
				r=mid-1;
			}
		}cout<<r<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值