最大子矩阵——枚举就能满分的Bug

最大子矩阵
【题目描述】
已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1×1)子矩阵。

比如,如下4×4的矩阵

0 -2 -7 0

9 2 -6 2

-4 1 -4 1

-1 8 0 -2
的最大子矩阵是

9 2

-4 1

-1 8
这个子矩阵的大小是15。

【输入】
输入是一个N×N的矩阵。输入的第一行给出N(0<N≤100)。再后面的若干行中,依次(首先从左到右给出第一行的N个整数,再从左到右给出第二行的N个整数……)给出矩阵中的N2个整数,整数之间由空白字符分隔(空格或者空行)。已知矩阵中整数的范围都在[−127,127]。

【输出】
输出最大子矩阵的大小。

【输入样例】
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
【输出样例】
15
【来源】
一本通在线评测
咳咳!各位,都来看一下这道题!
第一反应应该都是动态规划吧。
但是!容我再看一下数据!N(0<N≤100)也就是说,枚举只要不超过四层(100^4=100000000),就绝对不会超时。同理,枚举在理论上是可以拿到满分的!
但怎么拿呢?
首先
纯枚举的话,先枚举这个矩阵的头的坐标(t1,t2),再枚举矩阵尾的两个坐标,再累加这个矩阵的数字。将每个矩阵都枚举到。

for(int t1=1;t1<=n;t1++)
	{
		for(int t2=1;t2<=n;t2++)
		{
			for(int w1=1;w1<=n;w1++)
			{
				for(int w2=1;w2<=n;w2++)
				{
					for(int i=t1;i<=w1;i++)
					{
						for(int j=t2;j<=w2;j++)
						{
							sum[k]+=a[i][j];
						}
					} 
					maxn=max(maxn,sum[k]);
					k++;
				}
			}
		}
	}

But! 这是一个2+2+2的六重循环!部分分是可以拿到的,但满分绝对会TIE的!难道枚举终究拿不到满分吗?必须要启用动态规划吗?
等等!
前缀和,助我一臂之力!
前缀和,就是用许多和值加减,得到一些其他和值,这样就不用枚举每一个矩阵了!
嗯,可能说的有点抽象。不明白的话,看一下这个图。
在这里插入图片描述

这样的话求前缀和只需要枚举所有尾的坐标,就是一个四重循环。在通过前缀和求值时,也只需要四重循环了。

for(int w1=1;w1<=n;w1++)
		{
			for(int w2=1;w2<=n;w2++)
			{
				for(int i=1;i<=w1;i++)
				{
					for(int j=1;j<=w2;j++{
						sum[w1][w2]+=a[i][j];
					}
				}
				for(int k=1;k<w1;k++)
				{
					for(int h=1;h<w2;h++)
					{
						maxn=max(maxn,sum[w1][w2]);//别忘了比较时也要比前缀和。
						maxn=max(maxn,sum[w1][w2]-sum[w1][h]-sum[k][w2]+sum[k][h]);
						maxn=max(maxn,sum[w1][w2]-sum[w1][h]);
						maxn=max(maxn,sum[w1][w2]-sum[k][w2]);
					}
				}
				
			}
	}

OK,满分,但是,不够beautiful!找前缀和时其实还可以再优化一下。
在这里插入图片描述

for(int w1=1;w1<=n;w1++)
		{
			for(int w2=1;w2<=n;w2++)
			{
				sum[w1][w2]=sum[w1-1][w2]+sum[w1][w2-1]-sum[w1-1][w2-1]+a[w1][w2];
				for(int k=1;k<w1;k++)
				{
					for(int h=1;h<w2;h++)
					{
						maxn=max(maxn,sum[w1][w2]);
						maxn=max(maxn,sum[w1][w2]-sum[w1][h]-sum[k][w2]+sum[k][h]);
						maxn=max(maxn,sum[w1][w2]-sum[w1][h]);
						maxn=max(maxn,sum[w1][w2]-sum[k][w2]);
					}
				}
				
			}
	}

加上输入输出,完美!

#include<bits/stdc++.h>
using namespace std;
int a[1010][1000],m,n,maxn=-100000000,sum[1000][1000];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++) 
		{
			cin>>a[i][j];
		}
	} 
	for(int w1=1;w1<=n;w1++)
		{
			for(int w2=1;w2<=n;w2++)
			{
				sum[w1][w2]=sum[w1-1][w2]+sum[w1][w2-1]-sum[w1-1][w2-1]+a[w1][w2];
				for(int k=1;k<w1;k++)
				{
					for(int h=1;h<w2;h++)
					{
						maxn=max(maxn,sum[w1][w2]);
						maxn=max(maxn,sum[w1][w2]-sum[w1][h]-sum[k][w2]+sum[k][h]);
						maxn=max(maxn,sum[w1][w2]-sum[w1][h]);
						maxn=max(maxn,sum[w1][w2]-sum[k][w2]);
					}
				}
				
			}
	}
	cout<<maxn;
    return 0;
}


在这里插入图片描述

下线!散会!各回各家!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值