To the Max
Given atwo-dimensional array of positive and negative integers, a sub-rectangle is anycontiguous sub-array of size 1*1 or greater located within the whole array. Thesum of a rectangle is the sum of all the elements in that rectangle. In thisproblem the sub-rectangle with the largest sum is referred to as the maximalsub-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 consistsof an N * N array of integers. The input begins with a single positive integerN 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 secondrow, left to right, etc. N may be as large as 100. The numbers in the arraywill be in the range [-127,127].
Output
Output the sum ofthe 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
分析题目后易知需用到动态规划。
最初的思路是从第一个元素开始,逐个开始查找最大的。但是子矩阵(sub-rectangle)可能出现在任何一个起始地址和结束地址。如果用这样的遍历,将会使得程序的时间复杂度非常大。
经过片刻思考,心生一计,计算全部的数据,然后开始逐个减少行或者列,找到最大的子矩阵(the maximalsub-rectangle)。这种不是从最小的地方开始遍历比较查找,而是从最大的数组,即全部数组,逐个减少,达到了可以从任何方向减少元素,这样达到了子矩阵位置的任意性的选择。
接下来考虑到实现问题,动态生成二维数组的方法,除了指针就是向量,最后决定用向量。整个数据便是vector<vector<int>>类型的一个二维向量。
在逻辑部分,设计成坐标形势记录子矩阵的起始位置和结束位置。用startX,startY,endX,endY表示。动态规划中的递归地定义最优值这一步便可以作如下处理:对于矩阵有4种方法使之减少行或列,加上不减少任何行和列,一共有五种情况。
/*
fivecase: start * end (start+row) *end (start+column) * end start * (end+row) start * (end+column)
*/
那么暂时的结果便是以上5个中的一个。然后把暂时的结果当成当前的父矩阵,继续求解其子矩阵。再从5个结果中选出最大的!
这便是我的最初设计和实现步骤。实现如代码1所示。
……
但是经过运行发现结果是错误的!
稍加思考便发现了程序里面最傻B的错误了。
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
例如以上的4*4矩阵,在经过我的程序运行后,得出的最大子矩阵是:
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
因为:
|
|
>
但是:
|
|
>
这说明,每次只减少一行或者一列是一个极其弱智的错误!
以上最初设计程序时最欠考虑的办法。
现在只有采取补救措施了。便给每次的计算过程一个degree变量,让每次减少degree(degree = 1,2,……N)。但是这样做不到减少1行再减少3列再减少2行这样灵活的操作。而且,这样貌似也没有怎么用到动态规划的精华了。
如果是每次减少一行或者一列的话都考虑减少的数量是1到N的话,那样时间复杂度将会更大!
最初的设计,至此告终!
在不断地纠正中,往往就能发现新的方法。
头脑中展现的一个新的思路是:使用贪心的思想,如果最边上的a(a=1,2,……n)行或a列,其行内或列内的元素和是一个负数,并且这一行或者列的负数的绝对值大于其他的a行或者列的负数的和的绝对值的话,那么这一行必定要去掉!直到找不到这样的最外面的a行或者a列。这便是贪心算法的思想。诚然,贪心算法的最重要一点便是证明贪心的正确性。关于证明,显而易见,如果最外表的a行是绝对值最大的负数的话,那么去掉后的子矩阵值肯定会变大,证毕。
那么接下来便是贪心的实现了。实现见代码2。
针对代码2,即减掉最边上的a行或列这种思路,可举一反例如下:
|
按照代码2的思路,会先减掉第一行,然后检查终止条件
最终得到的最大子矩阵是:
|
Actually,最大的子矩阵应该是:
|
理论上第二种思路错误,通过反例也可以看出,这种思路是错误的!
但这并不说明用动态规划的思路是错误的!
只说明,动态规划的第一步“分析问题的最优解,找出最优解的性质,并刻画其结构特征”做错了。
最后经过研究诸多网友的解法,方才领悟本题的考点所在(由于本人初学,自认为的考点~哈哈~)。部分内容转自网友:
1、首先考虑一维的最大子段和问题,给出一个序列a[0],a[1],a[2]...a[n],求出连续的一段,使其总和最大。
a[i]表示第i个元素
dp[i]表示以a[i]结尾的最大子段和
dp[i] = max{a[i],dp[i-1] + a[i]}
解释一下方程:
如果dp[i-1] > 0,则 dp[i] = dp[i-1] +a[i]
如果dp[i-1] < 0,则 dp[i] = a[i]
因为不用记录位置信息,所以dp[]可以用一个变量dp代替:
如果dp > 0,则dp += a[i]
如果dp < 0,则dp = a[i]
2、考虑二维的最大子矩阵问题
我们可以利用矩阵压缩把二维的问题转化为一维的最大子段和问题。因为是矩阵和,所以我们可以把这个矩形的高压缩成1,用加法就行了。
恩,其实这个需要自己画图理解,我的注释里写得很详细了,自己看吧。
#include<iostream>
using namespace std;
#include<vector>
int temp[101];
int map[101][101];
// 求最大字段的和
int max(int *arr, int n){
intmax = 0, dp = 0;
for(int i=0; i < n; i++)
if(dp < 0)
dp= arr[i];
else {
dp += arr[i];
if(dp > max)
max=dp;
}
returnmax;
}
int main(){
int N;
cin>>N;
int i,j, k;
for(i= 0; i < N; i++)
for(j = 0; j < N; j++)
cin>>map[i][j];
int maxAll=0,maxOne=0;
//一二层的两个循环用来依次将一个二维图压缩成一维数列
|
for(i = 0; i < N; i++)
{
//自上而下
memset(temp, 0, sizeof(temp));
for(j = i; j < N; j++)
{
//自上而下
for(k = 0; k < N; k++)
temp[k] += map[j][k];//自左到右
maxOne= max(temp, N);
if(maxOne> maxAll)
maxAll= maxOne;
}
}
cout<<maxAll<<endl;
return0;
}
对于程序的画图解释(以4*4的矩阵):
|
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
temp[0] temp[1] temp[2] temp[3]
8 0 -7 -8
1 1 1 2
1 1 -4 1
1 1 1 2
代码1:
#include<iostream>
#include<vector>
using namespace std;
int N = 4;
vector< vector<int> > rec;
int degree = 0;
int countVector(vector<vector<int> > recParent, int startX, int startY, int endX, int endY)
{
intsum = 0;
for(int i = startX; i < endX; i++)
{
for(int j = startY; j < endY; j++)
{
//cout<<recParent[i][j]<<" ";
sum+= recParent[i][j];
}
//cout<<endl;
}
returnsum;
}
int fun(int startX, int startY, int endX,int endY)
{
/*
fivecase: start * end (start+row) *end (start+column) * end start * (end+row) start * (end+column)
*/
intmax0 = 0; //start * end
intmax1 = 0; //(start+row) * end
intmax2 = 0; //(start+column) * end
intmax3 = 0; //start * (end-row)
intmax4 = 0; //start * (end-column)
intresult = 0;
max0= countVector(rec, startX, startY, endX, endY);
if((startX+degree) < N)
{
max1= countVector(rec, startX+degree, startY, endX, endY);
}
if((startY+degree) < N)
{
max2= countVector(rec, startX, startY+degree, endX, endY);
}
if((endX-degree) > -1)
{
max3= countVector(rec, startX, startY, endX-degree, endY);
}
if((endY-degree) > -1)
{
max4= countVector(rec, startX, startY, endX, endY-degree);
}
if(max0 >= max1 && max0 >= max2 && max0 >= max3&& max0 >= max4)
{
result= max0;
}
elseif (max1 >= max0 && max1 >= max2 && max1 >= max3&& max1 >= max4)
{
result= fun(startX+degree, startY, endX, endY);
}
elseif (max2 >= max0 && max2 >= max1 && max2 >= max3&& max2 >= max4)
{
result= fun(startX, startY+degree, endX, endY);
}
elseif (max3 >= max0 && max3 >= max1 && max3 >= max2&& max0 >= max4)
{
result= fun(startX, startY, endX-degree, endY);
}
elseif (max4 >= max0 && max4 >= max1 && max0 >= max2&& max0 >= max3)
{
result= fun(startX, startY, endX, endY-degree);
}
returnresult;
}
void main()
{
vector<int>v;
v.push_back(0);
v.push_back(-2);
v.push_back(-7);
v.push_back(0);
rec.push_back(v);
v.clear();/
v.push_back(9);
v.push_back(2);
v.push_back(-6);
v.push_back(2);
rec.push_back(v);
v.clear();/
v.push_back(-4);
v.push_back(1);
v.push_back(-4);
v.push_back(1);
rec.push_back(v);
v.clear();/
v.push_back(-1);
v.push_back(8);
v.push_back(0);
v.push_back(-2);
rec.push_back(v);
v.clear();/
//countthe sum of the maximal sub-rectangle.
/*
fivecase: start * end (start+row) *end (start+column) * end start * (end+row) start * (end+column)
*/
// intstartX = 0;
// intstartY = 0;
// intendX = N;
// intendY = N;
intres = 0;
while(degree < N)
{
inttemp = fun(0,0,N,N);
res= temp>res?temp:res;
degree++;
}
cout<<res<<endl;
// for(int i=0; i<rec.size(); i++)
// {
// for(int j=0;j<rec[i].size();j++)
// {
// cout<<rec[i][j]<<" ";
// }
// cout<<endl;
// }
}
代码2:
#include <iostream>
#include <vector>
using namespace std;
int A[10000];
int N;
vector< vector<int> > rec;
int degree = 1; //减掉的程度,即一次性减掉几行/列
void input()
{
cin >> N;
while(!cin) {
cout << "error. " <<endl;
cout << "reinput number: ";
cin.clear();
cin.sync();
cin >> N;
}
int arrayLength = N*N;
for (int k = 0; k < arrayLength; k++)
{
cin>>A[k];
}
int index = 0;
while (index < arrayLength)
{
vector<int> v;
for (int x = 0; x < N; x++)
v.push_back(A[index++]);
rec.push_back(v);
v.clear();/
}
// for (int i=0; i < rec.size(); i++)
// {
// for (int j=0;j < rec[i].size();j++)
// {
// cout<<rec[i][j]<<" ";
// }
// cout<<endl;
// }
}
int countVector(int startX, int startY, int endX, int endY)
{
int sum = 0;
for (int i = startX; i <= endX; i++)
{
for (int j = startY; j <= endY; j++)
{
//cout<<recParent[i][j]<<" ";
sum += rec[i][j];
}
//cout<<endl;
}
return sum;
}
//返回degree行的元素总和
int countStartRow(int row, int begin, int end)
{
int rlSum = 0;
int degreeTemp = degree;
while (degreeTemp > 0 && (row + degreeTemp - 1) < N )
{
for (int x = begin; x <= end; x++)
{
rlSum += rec[row + degreeTemp - 1][x];
}
degreeTemp--;
}
return rlSum;
}
//返回degree行的元素总和
int countEndRow(int row, int begin, int end)
{
int rlSum = 0;
int degreeTemp = degree;
while (degreeTemp > 0 && (row - degreeTemp + 1) > -1 )
{
for (int x = begin; x <= end; x++)
{
rlSum += rec[row - degreeTemp + 1][x];
}
degreeTemp--;
}
return rlSum;
}
//返回degree列的元素总和
int countStartColumn(int column, int begin, int end)
{
int rlSum = 0;
int degreeTemp = degree;
while (degreeTemp > 0 && (column + degreeTemp - 1) < N)
{
for (int x = begin; x <= end; x++)
{
rlSum += rec[x][column + degreeTemp - 1];
}
degreeTemp--;
}
return rlSum;
}
//返回degree列的元素总和
int countEndColumn(int column, int begin, int end)
{
int rlSum = 0;
int degreeTemp = degree;
while (degreeTemp > 0 && (column - degreeTemp + 1) > -1)
{
for (int x = begin; x <= end; x++)
{
rlSum += rec[x][column - degreeTemp + 1];
}
degreeTemp--;
}
return rlSum;
}
int fun(int startX, int startY, int endX, int endY)
{
int result = 0; //返回的结果
int startRow = 0; //起始行的元素总和
int startColumn = 0; //起始列的元素总和
int endRow = 0; //结束行的元素总和
int endColumn = 0; //结束列的元素总和
while (degree < N)
{
startRow = countStartRow(startX, startY, endY);
endRow = countEndRow(endX, startY, endY);
startColumn = countStartColumn(startY, startX, endX);
endColumn = countEndColumn(endY, startX, endX);
if (startRow >= 0 && startColumn >= 0 && endRow >= 0 && endColumn >= 0) //达到终止条件
{
//result = countVector(startX, startY, endX, endY);
if ( (endX - startX) > degree || (endY - startY) > degree)
degree++;
else
break;
}
else if (startRow < startColumn && startRow < endRow && startRow < endColumn)//减掉起始行
{
startX = (startX + degree) < N ? (startX + degree) : startX;
degree = 1;
continue;
}
else if (startColumn < startRow && startColumn < endRow && startColumn < endColumn)//减掉起始列
{
startY = (startY + degree) < N ? (startY + degree) : startY;
degree = 1;
continue;
}
else if (endRow < startRow && endRow < startColumn && endRow < endColumn)//减掉结束行
{
endX = (endX - degree) > 0 ? (endX - degree) : endX;
degree = 1;
continue;
}
else if (endColumn < startRow && endColumn < startColumn && endColumn < endRow)//减掉结束列
{
endY = (endY - degree) > 0 ? (endY - degree) : endY;
degree = 1;
continue;
}
else
{
result = -10000;
}
}
result = countVector(startX, startY, endX, endY);
return result;
}
void main()
{
input();
int res = fun(0,0,N-1,N-1);
cout<<res<<endl;
}