【石材切割】解题报告

PROBLEM2.石材切割


问题描述:

某人得到一块N*M个小格的矩形石材(可能是玉石),经专家分析,把这个矩形石材的每个小格都有一个价值(使用一个绝对值不大于10的整数来描述),现在将这块石材切割成两块矩形石材,注意,切割只能与该矩形边平行,也就是说不能把矩形的小格切碎,假设每块矩形石材的价值为该矩形中所有小格子价值之和。

问怎样切割,才能使得这两个矩形的价值乘积最大。如下图是一种比较好的切割方式。

输入格式:

输入文件BRICK.IN的第一行为2个正整数NM,表示石材被划分为N*M个格子。接下来N行,每行有M个整数,代表这个格子的价值。


输出格式:

输出文件BRICK.OUT只有一行,包含一个整数,为两个矩形的价值的最大乘积


输入样例

输出样例

34

-1-1 -1 -1

00 0 0

-1 -1-1 -1

16


数据范围

对于30%的数据,满足N,M≤5

对于100%的数据,满足N,M≤100。每个小格的伤害值的绝对值不超过10

一切数据及中间变量不超过longint范围。



此题调得甚为蛋疼。。

最开始的时候没有读懂题,以为就切一刀就行了,然后就分成了两个矩形,原来切的数量任意。

当时是WA50,人品也还是不错了。
这是一道最大子矩形和+枚举的题。说的是O(n^3),可是我怎么看怎么是O(n^4)。


先讲最大子矩形和。就是压缩的方法,枚举上下界或者左右界,对每一个上下界或左右界求一个最大子区间和。
这里要用到二维前缀和。(这里一开始一直没弄清楚,应该是第j列的前i行、第i行的前j列,而不是前i行前j列)

预处理出来之后,就可以用O(n^4)来枚举两个上下界,或两个左右界,求出最优解。

本题要注意的就是有可能最优解是两个最小的负数相乘得来。所以总共进行四次DP。出解

AC程序

//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;

long n;long m;
long num[110][110];
long sum[110][110];
long sum2[110][110];
long f[110][110];
long ans = -oo;

int main()
{
	freopen("brick.in","r",stdin);
	freopen("brick.out","w",stdout);
	
	scanf("%ld%ld",&n,&m);
	for (long i=1;i<n+1;i++)
	{
		for (long j=1;j<m+1;j++)
		{
			scanf("%ld",&num[i][j]);
			sum[i][j] = sum[i-1][j]+num[i][j];
			sum2[i][j] = sum2[i][j-1]+num[i][j];
		}
	}
	long max = -oo;
	for (long i=1;i<n+1;i++)
	{
		for (long j=i;j<n+1;j++)
		{
			f[i][j] = -oo;
			long x = sum[j][1]-sum[i-1][1];
			f[i][j] = x;
			max = x;
			for (long k=2;k<m+1;k++)
			{
				x = sum[j][k]-sum[i-1][k];
				if (max>0) max+=x;
				else max = x;
				
				if (f[i][j] < max)
					f[i][j] = max;
			}
		}
	}
	
	for (long i=1;i<n+1;i++)
	{
		for (long j=i;j<n+1;j++)
		{
			for (long k=j+1;k<n+1;k++)
			{
				for (long l=k;l<n+1;l++)
				{
					ans >?= f[i][j]*f[k][l];
				}
			}
		}
	}
	
	for (long i=1;i<m+1;i++)
	{
		for (long j=i;j<m+1;j++)
		{
			f[i][j] = -oo;
			long x = sum2[1][j]-sum2[1][i-1];
			f[i][j] = x;
			max = x;
			for (long k=2;k<n+1;k++)
			{
				x = sum2[k][j]-sum2[k][i-1];
				if (max>0) max+=x;
				else max = x;
				
				if (f[i][j] < max)
					f[i][j] = max;
			}
		}
	}

	for (long i=1;i<m+1;i++)
	{
		for (long j=i;j<m+1;j++)
		{
			for (long k=j+1;k<m+1;k++)
			{
				for (long l=k;l<m+1;l++)
				{
					ans >?= f[i][j]*f[k][l];
				}
			}
		}
	}
	long min = oo;
	for (long i=1;i<n+1;i++)
	{
		for (long j=i;j<n+1;j++)
		{
			f[i][j] = oo;
			long x = sum[j][1]-sum[i-1][1];
			f[i][j] = x;
			min = x;
			for (long k=2;k<m+1;k++)
			{
				x = sum[j][k]-sum[i-1][k];
				if (min<0) min+=x;
				else min = x;
				
				if (f[i][j] > min)
					f[i][j] = min;
			}
		}
	}
	for (long i=1;i<n+1;i++)
	{
		for (long j=i;j<n+1;j++)
		{
			for (long k=j+1;k<n+1;k++)
			{
				for (long l=k;l<n+1;l++)
				{
					ans >?= f[i][j]*f[k][l];
				}
			}
		}
	}
	
	for (long i=1;i<m+1;i++)
	{
		for (long j=i;j<m+1;j++)
		{
			f[i][j] = oo;
			long x = sum2[1][j]-sum2[1][i-1];
			f[i][j] = x;
			min = x;
			for (long k=2;k<n+1;k++)
			{
				x = sum2[k][j]-sum2[k][i-1];
				if (min<0) min+=x;
				else min = x;
				
				if (f[i][j] > min)
					f[i][j] = min;
			}
		}
	}
	
	for (long i=1;i<m+1;i++)
	{
		for (long j=i;j<m+1;j++)
		{
			for (long k=j+1;k<m+1;k++)
			{
				for (long l=k;l<m+1;l++)
				{
					ans >?= f[i][j]*f[k][l];
				}
			}
		}
	}
	
	printf("%ld",ans);
	return 0;
}




50分程序:
//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;

long n;long m;
long s1[110];
long s2[110];
long ss = 0;
long num[110][110];

int main()
{
	freopen("brick.in","r",stdin);
	freopen("brick.out","w",stdout);
	
	scanf("%ld%ld",&n,&m);
	for (long i=1;i<n+1;i++)
	{
		for (long j=1;j<m+1;j++)
		{
			scanf("%ld",&num[i][j]);
			s1[i] += num[i][j];
			s2[j] += num[i][j];
		}
		ss += s1[i];
		s1[i] += s1[i-1];
	}
	for (long i=2;i<m+1;i++)
	{
		s2[i] += s2[i-1];
	}
	
	long ans = -oo;
	
	for (long i=1;i<m;i++)
	{
		if (ans<s2[i]*(ss-s2[i]))
			ans=s2[i]*(ss-s2[i]);
	}
	
	for (long i=1;i<n;i++)
	{
		if (ans<s1[i]*(ss-s1[i]))
			ans=s1[i]*(ss-s1[i]);
	}
	printf("%ld",ans);
	return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值