首页 > 试题广场 >

最小面积子矩阵

[编程题]最小面积子矩阵
  • 热度指数:5378 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积)

输入描述:
每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K
接下来N行,每行M个数,表示矩阵每个元素的值


输出描述:
输出最小面积的值。如果出现任意矩阵的和都小于K,直接输出-1。
示例1

输入

4 4 10
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

输出

1
#include <iostream>
using namespace std;
int main()
{
    int N,M,K;
    while(cin>>N>>M>>K){
        int matrix[N][M];
        for(int i=0;i<N;i++)
            for(int j=0;j<M;j++)
                cin>>matrix[i][j];
        int minS=123123123;
        for(int i=0;i<N;i++){
            int b[M]={0};
            for(int j=i;j<N;j++){
                int linecount=j-i+1;
                for(int k=0;k<M;k++) b[k]+=matrix[j][k];
                int rowcount=0,k=0,temp=0;
                for(;k<M;k++){
                    temp+=b[k];    //temp为 从i~j行,rowcount~k列的总值
                    if(temp>=K){    //该子矩阵达到要求
                        if(minS>linecount*(k-rowcount+1)) //更新最小面积
                            minS=linecount*(k-rowcount+1);
                        if(minS==1){  //已达到所能达到最小值,剪枝
                            printf("1\n");
                            return 0;
                        }
                        k=rowcount++;  //从下一列开始从新计算
                        temp=0;
                    }
                }
            }
        }
        if(minS==123123123)  printf("-1\n");
        else printf("%d\n",minS);
    }
    return 0;
}


发表于 2020-04-26 10:49:37 回复(0)
// 类似于求最大子矩阵和,O(n*m*m)
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int AX = 1e2 + 66 ; int a[AX][AX]; int main() {     int n , m , K , x ;     scanf("%d%d%d",&n,&m,&K);     memset( a , 0 , sizeof(a) );     for( int i = 1 ; i <= n ; i++ ) {         for( int j = 1 ; j <= m ; j++ ) {             scanf("%d",&a[i][j]);             a[i][j] += a[i-1][j] ;         }     }     int res = INF ;     for( int i = 1 ; i <= n ; i++ ) {         for( int j = 1 ; j <= m ; j++ ) {             int sum = 0 ;             int area = 0 ;             for( int k = 1 ; k <= m ; k++ ) {                 int tmp = a[j][k] - a[i-1][k] ;                 if( ( sum < K && tmp <= 0 ) || ( sum >= K && tmp >= 0 ) ) {                     if( area ) k-- ;                     sum = 0 ;                     area = 0 ;                 } else {                     sum += tmp ;                     area += j - i + 1 ;                     if( sum >= K ) {                         res = min( res , area ) ;                         sum = 0 ;                         area = 0 ;                     }                 }             }         }     }     printf("%d\n",(res<INF?res:-1));     return 0 ; }
编辑于 2020-06-21 16:24:18 回复(2)
暴力法.....想知道我复杂度是不是算错了。这个代码算出来复杂度m^2*n^2也过了。
#include<iostream>
(720)#include<algorithm>

using namespace std;
int m,n,k;
int sumg[100][100] = { 0 };  //二维树状数组 注意树状数组下标从1开始
int sumr[100][100] = { 0 };  //子矩阵1,1到x,y的和,下标从1开始。下标为0的一律结果是0

int lowbit(int x) {
	return x & (-x);
}

void update(int a,int b,int c) { //更新二维树状数组
	for (int i = a; i <= m; i += lowbit(i))
		for (int j = b; j <= n; j += lowbit(j))
			sumg[i][j] += c;
}

int getsum(int a, int b) { //通过树状数组求子矩阵 1,1到x,y的和。
	int sum = 0;
	for (int i = a; i >0; i -= lowbit(i))
		for (int j = b; j >0; j -= lowbit(j))
			sum+=sumg[i][j];
	return sum;
}

bool find(int a, int b) { //返回是否能找到一个高为a,长为b的矩阵,和满足>=K 。因为用已经计算好的数组sumr来算的,复杂度为O(m*n),常数项应该比较低
	if (a > m)a = m;
	if (b > n)b = n;
	for (int i = 1; i <= m - a+1; i++) {
		for (int j = 1; j <= n - b+1; j++) {
			int tempsum = 0;
			tempsum = sumr[i+a-1][j+b-1] - sumr[i+a-1][j-1]- sumr[i -1][ j + b - 1]+sumr[i-1][j-1];
			if (tempsum >=k)return 1;
		}
	}
	return 0;
}

int main() {
	int sum;
	cin >> m >> n >> k;
	for (int i = 1; i < m+1; i++){ //这个for整个复杂度 m*n*log(n)*log(m)
		sum = 0;
		for (int j = 1; j < n+1; j++) {
			int n; cin >> n;
			if (n >= k) { cout << 1; return 0; } //单个元素就比k大了,直接返回
			update(i, j, n); //更新二维树状数组
		}
	}
	for (int i = 1; i <= m; i++) { //这个for整个复杂度也是 m*n*log(n)*log(m)
		for (int j = 1; j <= n; j++)
			sumr[i][j] = getsum(i, j);
	}
	int maxmn = max(m, n);
	int face=-1;
	int min=0x3f3f3f3f;
	for (int i = 1; i <= maxmn; i++) { //这个for整个复杂度是i,j的m*n再乘以find的m*n复杂度,但是find的m*n常数项很低,故可以不超时。114ms
		for (int j = 1; j <= maxmn;j++)
			if (find(i, j) || find(j, 1)) {
				int temp = i * j;
				if (temp < min) { min = temp; face = temp; }
				break;
			}
	}
	cout<< face;
	return 0;
}


编辑于 2020-03-12 13:08:14 回复(0)

经过各种debug,终于亲自写出来了,花费的一个下午是值得的。。。注释中的cout可以忽略(debug用的)
#include <bits/stdc++.h>
using namespace std;
int n,m,k,a[110][110],ans,sum,minnum=1<<30;
int GetMin(int tmp_array[]) {
	ans=1<<30;
	for(int i =0; i<n; i++) {
		sum=0;
		for(int j=i; j<n; j++) {
			sum+=tmp_array[j];
			if(sum>=k) {
				
				if(ans>j-i+1) { 
					ans=j-i+1;
			//	cout<<ans<<" "<<sum<<" "<<j<<" "<<i<<endl;
					break;
				}
			}
		}
	}
	return ans;

}
int main() {
	//freopen("1.txt","r",stdin);
	cin>>n>>m>>k;
	for(int i=0; i<n; i++)
		for(int j=0; j<m; j++) cin>>a[i][j];
	int tmp_array[110];
//		for(int i=0; i<n; i++)
//		for(int j=0; j<m; j++) cout<<a[i][j]<<endl;
	for(int i=0; i<n; i++) {
		memset(tmp_array,0,sizeof(tmp_array));
		for(int j=i; j<n; j++) {
			for(int t=0; t<m; t++) {
					tmp_array[t]+=a[j][t];
			}
//				for(int z=0; z<n; z++) cout<<tmp_array[z]<<" ";
//				cout<<endl;
			int len=GetMin(tmp_array);
			if(len==1<<30) continue;
	//	cout<<minnum<<" "<<j<<" "<<i<<" "<<len<<endl;
			minnum=min(minnum,len*(j-i+1));

		}
	}
	cout<<minnum<<endl;


	return 0;
}


发表于 2020-03-09 16:21:05 回复(4)
//参考博客https://blog.csdn.net/Jaster_wisdom/article/details/52153685
#include <iostream>
#define N 101
#define M 101
using namespace std;

int Mat[N][M];
int temp[N][M];

int main()
{
    int n,m,k;
    while(cin>>n>>m>>k){
        int ans=123123123;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                cin>>Mat[i][j];
                if(i==0){
                    temp[i][j]=Mat[i][j];
                }
                else{
                    temp[i][j]=0;
                }
            }
        }
        for(int i=1;i<n;i++)
            for(int j=0;j<m;j++){
                temp[i][j]=temp[i-1][j]+Mat[i][j];//将矩阵的每行的累加和用辅助矩阵temp进行存储
            }
        int tmp[m];
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){
                for(int k=0;k<m;k++){
                    if(i==0)
                        tmp[k]=temp[j][k];
                    else
                        tmp[k]=temp[j][k]-temp[i-1][k];
                }
                /*
                  求解一维数组中,和大于等于k的最短连续子序列的长度.
                  采用两个指针,一个指向当前处理的子序列的首部一个指向尾部。
                    ①如果当前序列的和小于k,end++
                    ②如果当前序列的和大于k,start++并减去首部的值
                  记录求解过程中出现的最短序列的长度
                */
                int sum=0;
                int start=0,end=0;
                while(end<m){
                    sum+=tmp[end];
                    while(sum>=k){
                        ans=min(ans,(end-start+1)*(j-i+1));//注意此处需要计算面积
                        sum-=tmp[start++];
                    }
                    end++;
                }
            }
        }
        ans==123123123?cout<<-1<<endl:cout<<ans<<endl;//不存在输出-1

    }
    return 0;
}
/*
4 4 100
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
*/

编辑于 2019-04-05 14:29:51 回复(0)
//压缩数组后求解,求解内容参考牛油 琛红de小鸡
#include<stdio.h>
#define N 101
#define MIN 100000000
int a[N][N]; //原始数组
int b[N];  //压缩后数组
int ansmin;  //最小面积
int min(int a,int b) {return a<b?a:b;}
void init(int m)  //初始化
{
    for(int i=1;i<=m;i++)
        b[i]=0;
}
void yasuo(int many,int start,int m)  //压缩数组
{
    int over=start+many;
    for(int i=start;i<over;i++)
        for(int j=1;j<=m;j++)
            b[j]+=a[i][j];
}
int emm(int i,int m,int k)  //求解最小值
{
    int start=1;
    int over=1;
    int tmpsub=b[start];
    int tmpmin=MIN;
    while(over<=m)
    {
        if(tmpsub>=k)   
        {
            tmpmin=(tmpmin,(over-start+1)*i);
            tmpsub-=b[start];
            start++;
        }
        else {
            over++;
            tmpsub+=b[over];
        } 
    }
    return tmpmin;
}
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        ansmin=MIN;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)  //矩阵是多少行压缩在一起的
            for(int j=1;j+i<=n+1;j++)   //矩阵压缩是从第几行开始的
            {
                init(m);
                yasuo(i,j,m);
                int tmpmin=emm(i,m,k);
                ansmin=min(ansmin,tmpmin);  //保存最小面积
            }
        if(ansmin==MIN) printf("-1\n");  //没有 就-1
        else printf("%d\n",ansmin);
    }
    return 0;
}
发表于 2018-03-13 11:29:36 回复(0)
/*最小面积子矩阵——元素和不小于k的面积的最小矩阵
思路:早上做了最大子矩阵这道题,学到压缩矩阵的方法(还是看保洁妹大佬的代码)
下午就尝试一下这个方法,真的是……不知道说些什么好啊,我是定义一个结构体,因为在想
那个行数的面积s的时候不可能每一个压缩矩阵都要共用一个s,所以就定义一个结构体保存每个压缩的
s如图
之后找最大连续子矩阵就 这部分代码 int sum=0;     int s=0;     for(j=0;j<m;j++){         if(sum<k){             sum+=buf[j].value;             s+=buf[j].s;         }     } 说出来你们可能不信,我提交好几次才成功了,因为  if(sum>=k){         if(s<min_s){             min_s=s;         }     } 这部分代码,真的坑啊如果遍历完所有的压缩项依然小于k,那么最小的还是INF,如果不做判断,就会
出现INF变成某个数(因为累加的时候s+=buf[j].s),这里真的要注意啊
*/
#include<stdio.h>
#define INF 1E9
int matrix[101][101];
int n,m,k,min_s;//n是行数,m是列数 
//int buf[101];//压缩行的矩阵 
typedef struct buff{
    int value;
    int s;
}buff;
buff buf[101];
void findSmin(int start,int lines){
    int j,i;
    for(j=0;j<m;j++) {
        buf[j].value=0;
        buf[j].s=0;
    }
    for(j=0;j<m;j++){
        for(i=0;i<lines;i++){//压缩line行 
            buf[j].value+=matrix[start+i][j];
            buf[j].s++;
//            printf("buf[%d]:%d s:%d ",j,buf[j].value,buf[j].s);
        }
        printf("\n");
    }
    int sum=0;
    int s=0;
    for(j=0;j<m;j++){
        if(sum<k){
            sum+=buf[j].value;
            s+=buf[j].s;
        }
    }
//    printf("此时的s:%d\n",s);
    if(sum>=k){
        if(s<min_s){
            min_s=s;
        }
    }
    
}
int main(){
    int i,j;
    min_s=INF;
    while(scanf("%d %d %d",&n,&m,&k)!=EOF){
        for(i=0;i<n;i++){
            for(j=0;j<m;j++){
                scanf("%d",&matrix[i][j]);
            }
        }
        for(i=1;i<=n;i++){
            for(j=0;j+i-1<n;j++){
//                printf("findSmin(%d,%d)\n",j,i);
                findSmin(j,i);//从j行开始连续合并i行 
            }
            printf("\n");
        }
        if(min_s==INF){
            printf("-1\n");
        }else{
            printf("%d\n",min_s);
        }
    }


发表于 2019-03-15 17:19:18 回复(1)
很明显的一点是:O(N^4)的复杂度是不行的,这里假设我们的 n,m同阶;
如果做过NOIp的那道最大加权矩阵的话,就很简单了;
我们可以将其进行压缩;(那道题目是压缩后然后简单的dp;)
即:将列的元素压在一维的数组上,然后Two-Pointer即可解决;
这是复杂度就会少一个N ,由于我们要查询区间和,用前缀和优化即可;
具体看代码,自认为还是比较好理解的;
挺简单的一道题目;
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<bitset>
#include<ctime>
#include<deque>
#include<stack>
#include<functional>
#include<sstream>
//#include<cctype>
//#pragma GCC optimize(2)
using namespace std;
#define maxn 200005
#define inf 0x7fffffff
//#define INF 1e18
#define rdint(x) scanf("%d",&x)
#define rdllt(x) scanf("%lld",&x)
#define rdult(x) scanf("%lu",&x)
#define rdlf(x) scanf("%lf",&x)
#define rdstr(x) scanf("%s",x)
typedef long long  ll;
typedef unsigned long long ull;
typedef unsigned int U;
#define ms(x) memset((x),0,sizeof(x))
const long long int mod = 1e9;
#define Mod 1000000000
#define sq(x) (x)*(x)
#define eps 1e-5
typedef pair<int, int> pii;
#define pi acos(-1.0)
//const int N = 1005;
#define REP(i,n) for(int i=0;i<(n);i++)
typedef pair<int, int> pii;

inline int rd() {
    int x = 0;
    char c = getchar();
    bool f = false;
    while (!isdigit(c)) {
        if (c == '-') f = true;
        c = getchar();
    }
    while (isdigit(c)) {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f ? -x : x;
}


ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a%b);
}
int sqr(int x) { return x * x; }



/*ll ans;
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) {
        x = 1; y = 0; return a;
    }
    ans = exgcd(b, a%b, x, y);
    ll t = x; x = y; y = t - a / b * y;
    return ans;
}
*/

int n, m, K;
int a[200][200];
bool fg;
int pre[maxn];
int dp[maxn];
int minn = inf;
void sol() {
    for (int i = 1; i <= n; i++) {
        ms(dp);
        for (int j = i; j <= n; j++) {
            ms(pre);
            for (int k = 1; k <= m; k++)
                dp[k] += a[j][k];
            for (int k = 1; k <= m; k++)pre[k] = pre[k - 1] + dp[k];
            int l = 1, r = 1;
            while (1) {
                if (r > m)break;
                while (pre[r] - pre[l - 1] < K&&r <= m)r++;
                if (pre[r] - pre[l - 1] >= K&&r<=m) {
                    minn = min(minn, (j - i + 1)*(r - l + 1));
                    fg = 1;
                }
                while (pre[r] - pre[l - 1] >= K && l <= r) {
                    minn = min(minn, (r - l + 1)*(j - i + 1));
                    l++;
                    fg = 1;
                }
            }
        }

    }
}
int main()
{
    //ios::sync_with_stdio(0);
    rdint(n); rdint(m); rdint(K);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++)rdint(a[i][j]);
    }
    sol();
    if (fg == 0)cout << -1 << endl;
    else {
        cout << minn << endl;
    }
    return 0;
}


发表于 2019-01-31 21:15:03 回复(0)
n行m列,复杂度是O(n*n*m)
思路就是把从i行到k行合并成一行(问题规模是n*n),然后求合并后的一维数组的最短的、和不小于k的子序列。求一维数组的最短XXX序列是这道题的关键,有一种复杂度为O(m)的方法。
对于有m个元素的一维数组a,设置两个指针from和to,初试都指向a[0]。from到to之间就是正在处理的子序列。当子序列和小于k,to就往后移动直到抵达末尾或是序列和大于等于k。然后from向后移直到序列和小于k(相当于序列和大于等于k的序列,不断缩短他直到序列和不再符合要求),此时的序列长度+1如果比最小长度小那就修改最小长度为这个值。不断循环移动to——移动from这个过程直到抵达序列末尾,任务完成。至于不存在大于等于k的子序列的情况可能需要处理一下。
#include <stdio.h>
#include <limits.h>
#define N 101
#define M 101

int martix[N][M];//输入的矩阵
int sum[M];//相邻的几行合并而成的一维数组
int m, n, k;

int Shortest(int sum[M])
{//求一维数组里面和不小于k的最短连续序列长度
    int from=0, to=0;
    int len=INT_MAX;
    int x=0;//from到to的序列和
    while(to<m)
    {
        while(x<k&&to<m)//序列和小于k那终点后移
        {
            x+=sum[to];
            to++;
        }
        while(x>=k&&from<to-1)//序列和不小于k那起点后移
        {
            if(to-from<len) len=to-from;//这是更短的序列,记下来
            x-=sum[from];
            from++;
        }
        if(from+1==to) return 1;//追尾了
    }
    return len;
}

int main()
{
    while(scanf("%d%d%d", &n, &m, &k)!=EOF)
    {
        for(int i=0; i<n; i++)//输入
        {
            for(int j=0; j<m; j++)
            {
                scanf("%d", &martix[i][j]);
            }
        }
        int area=INT_MAX;//满足条件的最小面积,先假设是无穷大
        for(int i=0; i<n; i++)//从i行开始合并
        {
            for(int j=0; j<m; j++) sum[j]=0;//清空sum数组
            for(int k=i; k<n; k++)//合并从i行到k行
            {
                for(int j=0; j<m; j++) sum[j]+=martix[k][j];
                int len=Shortest(sum);
                if(len!=INT_MAX)
                {//如果面积比已有的最小面积还小,那就修改最小面积
                    if(len*(k-i+1)<area) area=len*(k-i+1);
                }
            }
        }
        if(area==INT_MAX) printf("-1\n");
        else printf("%d\n", area);
    }
    return 0;
}

编辑于 2018-03-08 20:24:42 回复(7)
前缀和,时间复杂度 O(n^4) 以为过不了,结果过了,战斗爽
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int MAXN = 110;

int arr[MAXN][MAXN] = {0};
int brr[MAXN][MAXN];

int main() {
	int n, m, k;
	while (cin >> n >> m >> k) {
		memset(arr, 0, sizeof arr);
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cin >> brr[i][j];
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				arr[i][j] = arr[i][j - 1] + arr[i - 1][j] + brr[i][j] - arr[i - 1][j - 1];
			}
		}
		bool isOk = false;
		for (int i = 1; i <= n; i++) { // 宽
			for (int j = 1; j <= m; j++) { // 长
				for (int o = i; o <= n; o++) {
					for (int p = j; p <= m; p++) {
						if (arr[o][p] - arr[o - i][p] - arr[o][p - j] + arr[o - i][p - j] >= k ) {
							isOk = true;
							cout << i *j << endl;
							break;
						}
					}
					if (isOk) {
						break;
					}
				}
				if (isOk) {
					break;
				}
			}
			if (isOk) {
				break;
			}
		}
		if (!isOk) {
			cout << -1 << endl;
		}
	}
	return 0;
}


编辑于 2024-03-04 09:50:06 回复(0)
将第 i 行 j 的的矩阵值更新为以 0,0 为起点该点为终点范围内对应所有元素的值,则 以 a,b 和 c,d 为对角线内的矩阵元素和易得。
从面积为 2 开始循环,每次从矩阵长为 1 进行嵌套循环,找到总和大于 k 的一组值即可退出,并输出当前循环面积值。

#include <ios>
#include<iostream>

using namespace std;

int m, n, k;

int main(){
    cin >> m >> n >> k;
    int matrix[m+1][n+1];
    for(int i=0; i<m; ++i){
        for(int j=0; j<n; ++j){
            cin >> matrix[i+1][j+1];
            if(matrix[i+1][j+1]>=k){
                cout << 1;
                return 0;
            }
        }
    }
    for(int i=0; i<=m; ++i) matrix[i][0]=0;
    for(int j=0; j<=n; ++j) matrix[0][j]=0;
    
    // 添加第 0 行 0 列后,求和步骤可以和输入合并
    for(int i=1; i<=m; ++i){
        for(int j=1; j<=n; ++j){
            matrix[i][j] += (matrix[i-1][j]+matrix[i][j-1]-matrix[i-1][j-1]);
        }
    }
    
    if(matrix[m][n]<k){
        cout << -1;
        return 0;
    } 

    // int l=1, w=1;
    int min_s=100001, loop_s=2;
    while(loop_s<=m*n){
        for(int l=2; l<=loop_s && l<=m; ++l){
            if(loop_s % l !=0 || loop_s/l>n) continue;
            int w=loop_s/l;
            for(int i=1; i+l-1<=m; ++i){
                for(int j=1; j+w-1<=n; ++j){
                    int s=matrix[i+l-1][j+w-1] - matrix[i-1][j+w-1] - matrix[i+l-1][j-1] + matrix[i-1][j-1];
                    if(s>=k){
                        cout << loop_s;
                        return 0;
                    }
                }
            }
        }
        ++loop_s;
    }
    return 0;
}

发表于 2023-03-19 14:50:46 回复(0)
//考的二位前缀和,动态规划问题,之前出现过这类题。
#include "stdio.h"
#include "algorithm"
using namespace std;

int main(){
    int array[110][110];
    int area[110][110];
    int N,M,K;
    while (scanf("%d%d%d",&N,&M,&K) != EOF){
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < M; ++j) {
                scanf("%d",&array[i][j]);
            }
        }
        area[0][0] = array[0][0];
        for (int i = 0; i < N; ++i) {//计算二位前缀和
            for (int j = 0; j < M; ++j) {
                if(i == 0)
                    area[i][j] = area[i][j-1] + array[i][j];
                else if(j == 0)
                    area[i][j] = area[i-1][j] + array[i][j];
                else
                    area[i][j] = area[i-1][j]+area[i][j-1]-area[i-1][j-1]+array[i][j];
            }
        }
        int s,minS = N*M;bool flag = true;
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < M; ++j) {
                if(area[i][j] >= K){
                    flag = false;
                    break;
                }
            }
            if(!flag)
                break;
        }
        if(flag == true){//全都小于K
            printf("-1");
            return 0;
        }
        int temp1,temp2,temp3;
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < M; ++j) {
                for (int k = i; k < N; ++k) {
                    for (int l = j; l < M; ++l) {
                        s = area[k][l] - area[k][j] - area[i][l] + area[i][j];
                        if (s >= K){
                            if (i == k)
                                minS = min(minS,l-j);
                            else if(j == l)
                                minS = min(minS,k - i);
                            else
                                minS = min(minS,(k-i)*(l-j));
                        }
                    }
                }
            }
        }
        printf("%d\n",minS);
    }
}
发表于 2023-03-15 14:10:33 回复(0)
把矩阵按行压缩之后,再利用滑动窗口(列)找到和大于等于k的长度最小的连续子序列,此时列数即长度,乘上压缩的行数就是面积
#include <iostream>
#include<cstdio>
using namespace std;
int A[101][101];
int main() {
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<n;i++)
    for(int j=0;j<m;j++)
    scanf("%d",&A[i][j]);
    int min_size=10001;
    for(int i=0;i<n;i++)
    for(int j=i;j<n;j++){
        int min_len=110;
        int sum=0;
        int first=0,last=0;
        while(last<m){
            for(int k=i;k<=j;k++)
            sum+=A[k][last];
            while(first<=last&&sum>=k){
                min_len=min(min_len,last-first+1);
                for(int k=i;k<=j;k++)
                sum-=A[k][first];
                first++;
            }
            last++;
        }
        if(min_len!=110)
        min_size=min(min_si***_len*(j-i+1));
    }
    if(min_size==10001)
    printf("-1");
    else
    printf("%d",min_size);
}

发表于 2023-03-10 13:40:41 回复(0)
此题是力扣209长度最小的子数组的变形题,同时结合了最大子矩阵的题目变化。该题需要在行上设置双指针i, j遍历所有的行区间。在每个行区间计算同一列所有行的元素的和,最后利用求解长度最小子数组的方法计算该行区间下的最小长度,然后乘以行区间长度j -i+1得到面积,之后不断比较求得面积最小值。
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int main(){
    int n, m,k;
    cin >> n >> m >> k;
    vector<vector<int>> matrix(n, vector<int>(m, 0));
    for(int i = 0;i < n;i++)
        for(int j = 0;j < m;j++)
            cin >> matrix[i][j];
    int result = INT_MAX;
    for(int i = 0;i < n;i++){
        vector<int> every(m);
        for(int j = i;j < n;j++){ //i,j遍历行区间
            for(int k = 0;k < m;k++)//计算每列行区间元素之和
                every[k] += matrix[j][k];
            //----------------------以下是最小子数组的解法
            int p = 0, sum = 0, result_per_loop = INT_MAX;
            for(int q = 0;q < m;q++){
                sum += every[q];
                while(sum >= k){
                    int sublength = q - p + 1;
                    result_per_loop = result_per_loop > sublength ? sublength : result_per_loop;
                    sum -= every[p++];
                }
            }
            //------------------------
            //不断比较获得最终结果
            if(result_per_loop != INT_MAX){
                int area = (j - i + 1) * result_per_loop;
                result = result > area ? area : result;
            }
        }
    }
    cout << (result == INT_MAX ? -1 : result) << endl;
}


发表于 2022-05-16 19:58:54 回复(0)
先选出第 i 行和第 j 行,然后将这两行及其中间的元素压缩成一个以为数组,然后就转化成求满足条件的最短的连续子序列,用滑动窗口即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 101;
int n, m, k, matrix[maxn][maxn];

void init(){
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            cin>>matrix[i][j];
        }
    }
}

void work(){
    int area = INT32_MAX;
    for(int i = 0; i < n; i++){
        vector<int> s(m, 0);
        for(int j = i; j < n; j++){
            for(int a = 0; a < m; a++){
                s[a] += matrix[j][a];
            }
            // 此时已经得到了第 i 行与第 j 行之间每一列的和 s[k]
            // 可以用双指针或者说是滑动窗口来得到最小的矩形面积
            int sum = s[0];
            int low = 0, high = 1;  // 左闭右开
            while(high < m){
                if(sum >= k){
                    area = min(area, (high - low) * (j - i + 1));
                    sum -= s[low];
                    low++;
                }else{
                    sum += s[high];
                    high++;
                }
            }
        }
    }
    if(area == INT32_MAX) area = -1;
    cout<<area<<endl;
}

int main(){
    while(cin>>n>>m>>k){
        init();
        work();
    }
}


发表于 2022-03-06 16:38:08 回复(0)

前缀和解法,理解简单,方法粗暴

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<string>
using namespace std;
const int MAXN = 103;
int A[MAXN][MAXN]={0};
int minnum = MAXN*MAXN;
int n,m,k;
void judge(int a,int b){
    for(int i=a;i<=n;i++){
        for(int j=b;j<=m;j++){
            int temp = A[i][j]-A[i][b-1]-A[a-1][j]+A[a-1][b-1];
            if(temp>=k){
                int numbers = (i-a+1)*(j-b+1);
                minnum = min(minnum,numbers);
            }
        }
    }
}
int main(){

    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int temp;
            cin>>temp;
            A[i][j] = A[i][j-1]+A[i-1][j]-A[i-1][j-1]+temp;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            judge(i, j);
        }
    }
    if(minnum==MAXN*MAXN){
        minnum = -1;
    }
    cout<<minnum<<endl;

    return  0;
}
发表于 2021-08-08 12:02:20 回复(0)
纯笨蛋解法
#include<bits/stdc++.h>
using namespace std;
int n,m,K;//n为行数 m为列数
int ma[100][100];//matrix
int me[100];//merge
int mArea=INT_MAX;//最小面积
int main(){
    while(cin>>n>>m>>K){
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                cin>>ma[i][j];
            }
        }
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){//从第i行到第j行的子矩阵
                memset(me,0,sizeof(me));
                for(int k=0;k<m;k++){//从首列到最后一列依次merge
                    for(int l=i;l<=j;l++){//每列的第i行到第j行依次merge
                        me[k]+=ma[l][k];
                    }
                }
                //至此 从第i行到第j行的merge结束 
                //接下来要研究merge的连续子序列了
                //其中 j-i+1 为子矩阵的行数
                for(int k=0;k<m;k++){
                    //从merge[k]出发的子序列
                    //l-k+1 为子矩阵列数
                    int sum=0;
                    for(int l=k;l<m&&(j-i+1)*(l-k+1)<=mArea;l++){
                        sum+=me[l];
                        if(sum>=K){//当前子序列超过K了就别再往后看了
                            mArea=(j-i+1)*(l-k+1);
                            break;
                        }
                    }
                }
            }
        }
        if(mArea==INT_MAX)
            cout<<"-1"<<endl;
        else
            cout<<mArea<<endl;
    }
}

发表于 2021-03-20 10:55:42 回复(0)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int main()
{
	int n,m,x;
	while(cin>>n>>m>>x) {
		int h[110][110];
		int b[110];
		for(int i=0; i<n; i++) {
			for(int j=0; j<n; j++) {
				cin>>h[i][j];
			}
		}
		int dp[110][110];
		for(int i=0; i<m; i++)
			dp[0][i]=h[0][i];
		for(int i=1; i<n; i++) {
			for(int j=0; j<m; j++) {
				dp[i][j]=dp[i-1][j]+h[i][j];
			}
		}
		int minx=99999999;
		for(int i=0; i<n; i++) {
			for(int j=i; j<n; j++) {
				for(int o1=0; o1<m; o1++) {
					int num=0;
					for(int k=o1; k<m; k++) {
						if(i==0)
							b[k]=dp[j][k];
						else
							b[k]=dp[j][k]-dp[i-1][k];
						num+=b[k];
						if(num>x){
							int sum=(j-i+1)*(k-o1+1);
							if(sum<minx)
								minx=sum;
						}
					}
				}
			}
		}
		cout<<minx<<endl;
	}
	return 0;
}

发表于 2021-02-03 16:52:18 回复(0)
补充一个测试例子
3 3  6
1 1 2
2 1 2
2 1 2
正确输出: 3
我的程序输出 4, 也能通过。说明评判系统没有考虑这种竖状的矩阵。

算法实现,参考保洁妹大佬的
1. 将矩阵 r*n(r=1,2,,,n) 压缩乘以为数组A
2. 求 A 中连续和大于等于 k 的最短长度l
得最小面积 r*l

#include <iostream>
#include <string>
#include <vector>
#include <limits.h>

int MinArea(std::vector<std::vector<int>>& matrix, int n, int m, int k){
    std::vector<std::vector<int>> tmp;
    for (int i = 0; i < n; i++){
        tmp.push_back(std::vector<int>(m, 0));
    }
    int min_area = INT_MAX;

    for (int rows = 1; rows <= n; rows++){
        for (int i = n-1; i-rows+1 >= 0; i--){
            for (int j = 0; j < m; j++){
                tmp[i][j] = tmp[i][j] + matrix[i-rows+1][j];
            }
            int left = 0;
            int right = 0;
            int sum = 0;
            while(left < m && right < m+1){
                if (sum < k){
                    sum = sum + tmp[i][right];
                    right++;
                }else{
                    if ((right-left)*rows < min_area){
                        min_area = (right-left)*rows;
                    }
                    sum = sum - tmp[i][left];
                    left++;
                }
            }
        }
    }
    
    return min_area;
}

int main(){
    std::string N, M, K;
    std::string V;
    while(std::cin >> N >> M >> K){
        int n = std::stoi(N);
        int m = std::stoi(M);
        int k = std::stoi(K);
        std::vector<std::vector<int>> matrix;
        for (int i = 0; i < n; i++){
            std::vector<int> row;
            for (int j = 0; j < m; j++){
                std::cin >> V;
                row.push_back(std::stoi(V));
            }
            matrix.push_back(row);
        }
        
        int min_area = MinArea(matrix, n, m, k);
        std::cout << min_area << std::endl;
        
    }
    
    return 0;
}


编辑于 2021-02-03 13:02:51 回复(0)
#include<iostream>
#include<climits>
#include<algorithm>
#include<cstring>
using namespace std;

int N, M, K;
int** arr;
void init() {
	arr = new int* [N + 1];
	for (int i = 0; i <= N; i++) {
		arr[i] = new int[M + 1];
		fill(arr[i], arr[i]+M+1, 0);
	}
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			cin >> arr[i][j];
			arr[i][j] += arr[i - 1][j];
		}
	}
}
// 获取从i到j行的行相加的压缩一维数组
int* getSubArr(int i, int j) {
	int* subArr = new int[M + 1];
	fill(subArr, subArr+M+1, 0);
	for (int k = 1; k <= M; k++) {
		subArr[k] = arr[j][k] - arr[i][k];
	}
	return subArr;
}
int main() {
	while (cin >> N >> M >> K) {
		init();
		int minArea = INT_MAX;
		bool flag = false;
		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j <= N; j++) {
				int* subArr = getSubArr(i, j);
				int* subArrCopy = new int[M + 1];
				int* begin = new int[M + 1];
				fill(begin, begin+M+1, 0);
				for (int t = 0; t <= M; t++) {
					begin[t] = t;
					subArrCopy[t] = subArr[t];
				}
				for (int k = 1; k <= M; k++) {
					if (subArr[k] >= K) {
						minArea = min(minArea, j - i);
						flag = true;
						continue;
					}
					if (subArr[k - 1] + subArr[k] > subArr[k]) {
						subArr[k] = subArr[k - 1] + subArr[k];
						begin[k] = begin[k - 1];
						if (subArr[k] >= K) {
							int sum = 0;
							for (int u = k; u >= begin[k]; u--) {
								sum += subArrCopy[u];
								if (sum >= K) {
									minArea = min(minArea, (j - i) * (k - u + 1));
									flag = true;
									break;
								}
							}
						}
					}
				}
			}
		}
		if (!flag) {
			cout << -1 << endl;
		}
		else {
			cout << minArea << endl;
		}
	}
	return 0;
}

发表于 2020-06-21 15:11:54 回复(0)

问题信息

难度:
29条回答 11726浏览

热门推荐

通过挑战的用户

查看代码