To the Max
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 52190 Accepted: 27587
Description
Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle.
As an example, the maximal sub-rectangle of the array:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:
9 2
-4 1
-1 8
and has a sum of 15.
Input
The input consists of an N * N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N^2 integers separated by whitespace (spaces and newlines). These are the N^2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].
Output
Output the sum of the maximal sub-rectangle.
Sample Input
4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1
8 0 -2
Sample Output
15
做法有很多,O(n^4)可以过,直接暴力求解,O(n^3)转化成最大子段和求解
暴力求解代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 105;
int sum[N][N];
int a[N][N];
bool judge(int i,int j)
{
if(i < 0 || j < 0){
return false;
}
else{
return true;
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i = 0;i < n;++i)
{
for(int j = 0;j < n;++j)
{
scanf("%d",&a[i][j]);
}
}
for(int i = 0;i < n;++i)
{
for(int j = 0;j < n;++j)
{
sum[i][j] = a[i][j];
if(judge(i - 1,j - 1)){
sum[i][j] -= sum[i - 1][j - 1];
}
if(judge(i - 1,j)){
sum[i][j] += sum[i - 1][j];
}
if(judge(i,j - 1)){
sum[i][j] += sum[i][j - 1];
}
}
}
int MAX = -1;
for(int i = 0;i < n;++i)
{
for(int j = 0;j < n;++j)
{
for(int x = 0;x <= i;++x)
{
for(int y = 0;y <= j;++y)
{
int z = sum[i][j];
if(judge(x - 1,y - 1)){
z += sum[x - 1][y - 1];
}
if(judge(x - 1,j)){
z -= sum[x - 1][j];
}
if(judge(i,y - 1)){
z -= sum[i][y - 1];
}
if(z > MAX){
MAX = z;
}
}
}
}
}
printf("%d\n",MAX);
}
return 0;
}
首先先了解一下最大子串和
N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
Output
输出最大子段和。
Input示例
6
-2
11
-4
13
-5
-2
Output示例
20
类似的解法:
令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含
如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大
再看这道题,显然是把最大子段和转成二维形式了,先算出i行到j行对应的列的各自的和放在一个数组中s,在两重循环遍历所有组合情况,然后再用一维求最大子段和。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 105;
int dp[N][N];
int s[N][N];
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
memset(s,0,sizeof(s));
for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= n;++j)
{
scanf("%d",&dp[i][j]);
}
}
for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= n;++j)
{
s[j][i] = s[j - 1][i] + dp[j][i];
}
}
int MAX = -1;
for(int i = 0;i <= n;++i)
{
for(int j = i + 1;j <= n;++j)
{
//遍历所有情况组合
int b = 0;
for(int k = 1;k <= n;++k)
{
if(b > 0){
b += (s[j][k] - s[i][k]);
}
else{
b = s[j][k] - s[i][k];
}
if(MAX < b){
MAX = b;
}
}
}
}
printf("%d\n",MAX);
}
return 0;
}