108 Maximum Sum

/*
推荐题型:四星。    动态规划

题意:输入矩形长度N,矩形中节点上的数值A[i][j],输出子矩形所包含节点的数值之和的最大值。

动态规划策略:
状态:d[lx][ly][rx][ry]表示矩形(lx,ly,rx,ry)中的最大值。
状态转移方程:d[lx][ly][rx][ry]=max(d[lx+1][ly][rx][ry],d[lx][ly+1][rx][ry],d[lx][ly][rx-1][ry],d[lx][ly][rx][ry-1],All())  //其中All()表示矩形中所有节点之和。结果超时,效率为O(N*N*N*N)

看别人代码后,分阶段进行动态规划,行数逐渐增加,可以定一个状态(设最大行数为k,且子矩形下边必为k),然后历编子矩形上边i,这两个状态确定后,对列进行判断,使用数组d[j]表示右边为j的最大值,这样只需搜索一遍就可以找到,上下边确立后子矩形的最大值。可讲效率缩小到O(N*N*N)

主要改进:使用了中间状态,确立矩形的下边,右边,从而提高效率,不过还不会用。。。
*/

#include <cstdio>
#include <cstring>
const int nMax=107;
const int INF=0x7fffffff;
int A[nMax][nMax];
int sum[nMax][nMax];
int d[nMax][nMax][nMax][nMax];
int N;
void init()
{
	scanf("%d", &N);
	int i, j;
	for(i = 1; i <= N; ++ i)
	{
		sum[i][0] = 0;
		for(j = 1; j <= N; ++ j)
		{
			scanf("%d", &A[i][j]);
			sum[i][j] = sum[i][j-1] + A[i][j];
		}
	}
}
int max(int a,int b)
{
	return a > b ? a : b;
}
/*int dp(int lx, int ly, int rx, int ry)
//递归实现
{
	if(lx > rx || ly > ry) return -INF;
	int &temp = d[lx][ly][rx][ry];
	if(temp != -1) return temp;
	temp = -INF;
	temp=max(temp,dp(lx+1,ly,rx,ry));
	temp=max(temp,dp(lx,ly+1,rx,ry));
	temp=max(temp,dp(lx,ly,rx-1,ry));
	temp=max(temp,dp(lx,ly,rx,ry-1));
	int c=0;
	for(int i=lx;i<=rx;i++)
		c+=sum[i][ry]-sum[i][ly-1];
	temp=max(temp,c);
	return temp;
}*/
void dp()
	//非递归实现,仍然超时。
{
	int kx,ky,x,y;
	for(kx = 0; kx < N; ++ kx)//行的增量
		for(ky = 0; ky < N; ++ ky)//列的增量
		{
			for(x = 1; x + kx <= N; ++ x)//子矩形左上角坐标
				for(y = 1; y + ky <= N;++ y)
				{
					if(0 == kx)
					{
						d[x][y][x+kx][y+ky] = sum[x][y+ky] - sum[x][y-1];
					}
					else if(0 == ky)
					{
						int a = 0;
						for(int i = x; i <= x + kx; ++ i)
							a += A[i][y];
						d[x][y][x+kx][y+ky] = a;
					}
					else
					{
						int max1 = -INF;
						max1 = max(max1, d[x+1][y][x+kx][y+ky]);
						max1 = max(max1, d[x][y+1][x+kx][y+ky]);
						max1 = max(max1, d[x][y][x+kx-1][y+ky]);
						max1 = max(max1, d[x][y][x+kx][y+ky-1]);
						int c=0;
						for(int i = x;i <= x + kx;i++)
							c+=sum[i][y + ky]-sum[i][y-1];
						max1 = max(max1, c);
						d[x][y][x+kx][y+ky] = max1;
					}
				}
		}
}
int main()
{
	//freopen("f://data.in","r",stdin);
	init();
	//dp(1, 1, N, N);
	dp();
	printf("%d\n",d[1][1][N][N]);
	return 0;
}



//方法二:改进后的动态规划
#include <cstdio>
#include <cstring>
const int nMax=107;
const int INF=0x7fffffff;
int A[nMax][nMax];
int N;
int ans;
void init()
{
	scanf("%d", &N);
	int i, j;
	for(i = 1; i <= N; ++ i)
		for(j = 1; j <= N; ++ j)
			scanf("%d", &A[i][j]);
	ans = -INF;
}
int max(int a,int b)
{
	return a > b ? a : b;
}
void dp(int k)
{
	int a[nMax],d[nMax];
	memset(a,0,sizeof(a));
	memset(d,0,sizeof(d));
	for(int i = k; i >= 1; -- i)
		for(int j = 1; j <= N; ++ j)
		{
			a[j] += A[i][j];
			d[j] = max(d[j-1] + a[j], a[j]);
			if(d[j] > ans)
				ans = d[j];
		}
}
int main()
{
	//freopen("f://data.in","r",stdin);
	init();
	for(int i = 1; i <= N; ++ i)
		dp(i);
	printf("%d\n",ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值