hdu 1505 /hdu 1506(最大子矩阵)

题意都是求最大子矩阵和。

自己用O(n^3)的算法,直接给我无情的TLE (T..><..T)

随后看到了这个位置迭代算法。

left[i] 表示在第i号元素的左边从left[i]至i中, elem[j] >= elem[i] ( left[i]<=j<=i) ;这样就可以立即知道它左边最大合适的宽度,而高度就是elem[i],所以左边的面积就是elem[i]*(i-left[i]+1)。

同理可得,用right[i]表示在第i号元素的右边从i->>>right[i] 中,elem[j]>=elem[i] ( i<=j<=right[i]),所以右边的面积就是elem[i]*(right[i]-i+1).

但是上面包含了两个elme[i],就是第i号元素取了两次。

总面积就为:area[i]=elem[i] *(right[i]-left[i]+1).

最后的方程就变成了Maxelem=max(area[j]) ) ( 1<=j<=n).

至于left,与right的求法如以下代码:它们的顺序各有不同,为什么?自己思考。

	for(i=1;i<=m;i++)
		while(num[left[i]-1]>=num[i])
			left[i]=left[left[i]-1];
	for(i=m;i>=1;i--)
		while(num[right[i]+1]>=num[i])
			right[i]=right[right[i]+1];

但是我们还要保证它不会无限循环,因为它的最小值是0,可以取小于0的数当作界限点。这里我们取-1。

hdu1506(hdu1505是它的加强版,在下面会讲)代码如下:

#include<cstdio>

#define M 100010
#define FLAG -1
#define Max(a,b) (a)>(b)?(a):(b)

__int64 elem[M]; 

int main(){

	int n,i,right[M],left[M];
	while(scanf("%d",&n) && n ){
		
		for(i=1;i<=n;i++){
			scanf("%I64d",&elem[i]);
			right[i]=left[i]=i;
		}

		elem[0] = elem[n+1] = FLAG;

		for(i=1;i<=n;i++)
			while(elem[left[i]-1]>=elem[i])
				left[i]=left[left[i]-1];

		for(i=n;i>=1;i--)
			while(elem[right[i]+1]>=elem[i])
				right[i]=right[right[i]+1];

			__int64 maxMatrix=0;

		for(i=1;i<=n;i++)
			maxMatrix=Max(maxMatrix,(__int64)(right[i]-left[i]+1)*elem[i]);

		printf("%I64d\n",maxMatrix);
	}
	return 0;
}


对于Hdu 1505
我们采用一个num[i]数字来存储从第一行到当前行的合理值,如果中间出现R,就从R的下一个开始算,如果当前是R就赋值为0,否则即为当前值(其实就是上一行)++。

然后采用上题的方式求解。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

#define M 1005
#define MAX(a,b) (a)>(b)?(a):(b)

char s[M*3];
int n,m,T,num[M],Max;

void dp(){

	int left[M],right[M],i;
	for(i=1;i<=m;i++)
		left[i]=right[i]=i;
	for(i=1;i<=m;i++)
		while(num[left[i]-1]>=num[i])
			left[i]=left[left[i]-1];
	for(i=m;i>=1;i--)
		while(num[right[i]+1]>=num[i])
			right[i]=right[right[i]+1];
			
	for(i=1;i<=m;i++)
			Max=MAX((right[i]-left[i]+1)*num[i],Max);
}			
void init(){
	
	scanf("%d %d",&n,&m);
	memset(num,0,sizeof(num));
	num[0]=num[m+1]=-1;
	Max=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%s",s);
			s[0]=='F'?num[j]++:num[j]=0;
		}
		dp();
	}
}

int main(){

	scanf("%d",&T);
	while(T--){
		init();
		cout<<3*Max<<endl;
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值