hdu1081To The Max(dp)

来源:互联网 发布:双防火墙网络环境搭建 编辑:程序博客网 时间:2024/06/04 00:49

题目请戳这里

题目大意:给一个n*n的矩阵,矩阵每个元素为一个整数,求权值和最大的子矩阵。

题目分析:这是求最大连续子序列和的二维版本。所以我们可以先看看一维情况:求一个序列的最大连续子序列和。

很显然可以朴素O(n^2)解决这个问题,但是如果我们利用前缀和的话,可以做到O(n)。dp[i]表示前i个数最大连续子序列。那么有方程:dp[i] = max(dp[i - 1] + a[i],a[i])。即要么这个最大连续子序列包含a[i]或者不包含a[i]。

然后再回到二维情况:很显然可以朴素O(n^4)解决这个问题。枚举矩阵的所有子矩阵就可以了。不过这样时空复杂度太高,不妨参照一维情况优化:枚举前i行,dp[i][j][k]表示前i行从第j列到第k列最大子矩阵。那么显然有dp[i][j][k] = max(dp[i - 1][j][k] + a[i][k] - a[i][j - 1],a[i][k] - a[i][j - 1]),其中a[i][j]表示原矩阵中第i行前j列和。这样问题就可以O(n^3)时间解决了。

再仔细看看转移方程,不难发现其实dp数组还可以省掉一维,因为每个dp[i][j][k]只需要知道dp[i - 1][j][k]就可以了,所以dp数组第一维可以去掉。

详情请见代码:

#include <iostream>#include<cstdio>#include<cstring>using namespace std;const int N = 101;int lcm[N][N],dp[N][N][N];int n;int main(){    int i,j,k;    while(scanf("%d",&n) != EOF)    {        memset(dp,0,sizeof(dp));        memset(lcm,0,sizeof(lcm));        for(i = 1;i <= n;i ++)            for(j = 1;j <= n;j ++)                scanf("%d",&lcm[i][j]),lcm[i][j] += lcm[i][j - 1];        int ans = -100000;        for(i = 1;i <= n;i ++)        {            for(j = 1;j <= n;j ++)            {                for(k = j;k <= n;k ++)                {                    dp[i][j][k] = max(lcm[i][k] - lcm[i][j - 1],lcm[i][k] - lcm[i][j - 1] + dp[i - 1][j][k]);                    ans = max(dp[i][j][k],ans);                }            }        }        printf("%d\n",ans);    }    return 0;}


0 0