问题描述
poj1050 to the max 问题可简要描述如下:
给定一个整数n(n<100),之后输入n*n个数字组成一个方阵,在方阵内部可以选出一些小矩阵,求在方阵内部可以选出的小矩阵所包含数字的和的最大值
这个问题是一个典型的动态规划的问题,而且,和最优子序列问题实质上是一样的。
最优子序列问题
最优子序列问题是指,给定一组序列,求解其子列中所有元素和的最大值。子列为其中某个元素到另一个元素之间的连续的一组值。比如给定数组a[6]={-3,5,2,-4,9,-6},其中{5,2,-4}就可以作为a的一个子列,不过,显然可以看出此例最优的子列应该为{5,2,-4,9},和是12。下面分析其解决算法:
暴力枚举
显然,可以通过枚举的方法,把整个数组的每一种子列的和全搜索一遍,并记录最大值输出,相关代码如下
int maxnum=0;//记录和的最大值
int a[]={-3,5,2,-4,9,-6};
int n=sizeof(a)/sizeof(int);
for(int i=0;i<n;i++){
int sum=0;
for(int j=i;j<n;j++){
sum+=a[j];//求出从a[i]到a[j]子列的和
if(sum>maxnum)
maxnum=sum;
}
}
易看出,此算法的时间复杂度是O(n2)
动态规划
首先定义一状态量:每个位置 i 的最优子序列 bi 的值就是以此数为结尾的所有子序列的值的最大值;也就是对a[6]={-3,5,2,-4,9,-6},第二个位置的最优子序列值就是{-3,5}与{5}两个子序列其中值最大的那个,即b2=5。那么,不难理解,若已知 bi ,则有递推公式: bi+1 = bi + a [ i+1 ],bi >= 0 或 bi+1 = a [ i+1 ], bi<0 。
故求解所有子序列中的最大值,即为求解序列b1 到bn 的最大值,代码如下:
int a[]={-3,5,2,-4,9,-6};
int n=sizeof(a)/sizeof(int);
int maxhere=0, maxnum=0; //maxhere记录的是当前位置的最优子序列值,没有必要另用数组保存
for (int i = 0; i < n; i++){
if (maxhere >= 0)
maxhere += a[i];
else
maxhere = a[i];
if (maxofnum < maxhere)
maxofnum = maxhere;
}
不难看出时间复杂度是O(n)
poj1050
回到问题poj1050,题目说要求得内部矩阵的值的最大值,如果要暴力枚举的话,从列的角度需要两次循环,行的角度一样,整个 程序较为复杂,因此也要使用dp算法。
事实上按照如上定义的状态量来计算也并不简单,因为二维之间的状态量之间没有一维那样明显的递推关系。可以将二者结合起来,将从第i行到第j行之间的数字按列相加,得一列数字,之后使用上面代码,计算此列数字中的最大子列值,有如下代码:
int maxsequence(int b[], int nb){ //计算一组数中的最优子序列的值
int maxhere=0, maxofnum=0;
for (int i = 0; i < nb; i++){
if (maxhere >= 0)
maxhere += b[i];
else
maxhere = b[i];
if (maxofnum < maxhere)
maxofnum = maxhere;
}
return maxofnum;
}
for (int i = 0; i < n; i++){
int temp[110] = { 0 }; //将第i行到第j行的数据按列相加,记录在temp[]中
for (int j = i; j < n; j++){
for (int k = 0; k < n; k++){
temp[k] += a[j][k];
}
if (max < maxsequence(temp, n))
max = maxsequence(temp, n);
}
}
AC代码
#include<iostream>
using namespace std;
int maxsequence(int b[], int nb){
int maxhere=0, maxofse=0;
for (int i = 0; i < nb; i++){
if (maxhere >= 0)
maxhere += b[i];
else
maxhere = b[i];
if (maxofse < maxhere)
maxofse = maxhere;
}
return maxofse;
}
int main(){
int n;
cin >> n;
int **a = new int *[n];
for (int i = 0; i < n; i++)
a[i] = new int[n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++){
cin >> a[i][j];
}
int max = 0;
for (int i = 0; i < n; i++){
int temp[110] = { 0 };
for (int j = i; j < n; j++){
for (int k = 0; k < n; k++){
temp[k] += a[j][k];
}
if (max < maxsequence(temp, n))
max = maxsequence(temp, n);
}
}
for (int i = 0; i < n; i++)
delete[]a[i];
delete[]a;
cout << max<<endl;
return 0;
}