To the Max-动态规划 树状数组

To the Max
问题来源:hdu-1050

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

源代码一(普通DP):
#include<iostream>
using namespace std;

const int MAX = 100;
int map[MAX][MAX];
int s[MAX] , num , N;

int dp( void );

int main( ){
    int ans , max;

    while( cin>>N ){
        max = 0;
        //数组下表从0开始
        for( int i=0 ; i<N ; i++ )
            for( int j=0 ; j<N ; j++ )
                cin>>map[i][j];

        for( int i=0 ; i<N ; i++ ){    //i遍历每一行
            for( int j=i ; j<N ; j++ ){    //j遍历i之后的每一行
                for( int m=0 ; m<N ; m++ ){    //m是对于每一列来说
                    s[m] = 0;    //初始化
                    for( int k=i ; k<=j ; k++ )
                        s[m] += map[k][m];    //叠加和
                }
                ans = dp( );    //求最大子序列之和
                if( ans>max )
                    max = ans;
            }
        }
        cout<<max<<endl;
    }

    return 0;
}

int dp( void ){
    int sum=0 , max=0;

    for( int i=0 ; i<N ; i++ ){    //O(n)的复杂度
        sum += s[i];
        if( sum < 0 )    //小于0就赋值为0
            sum = 0;
        if( sum > max )
            max = sum;    //记录最大值
    }
    return max;
}

代码分析:从上面的这段代码我们能很明显地看出时间复杂度很高,主函数里for嵌套4层,并且求和的阶段s[m]+=map[k][m]是最频繁的一句代码,求和部分很频繁,为了节约时间,我改成了二维的树状数组来实现求和,因为树状数组求和的复杂度时O(logn)的,比起O(n)的普通求和节约了不少时间

源代码二(二维树状数组求和):
#include<iostream>
using namespace std;

const int MAX = 100;
int map[MAX][MAX];
int s[MAX] , C[MAX][MAX] , num , N;
//C是求和的累加数组

int  dp( void );
int  lowbit( int n );    //管辖域
int  sum( int col , int n );    //返回A[1]到A[n]的和
void add( int k , int col , int n );    //更新C数组的个元素

int main( ){
    int ans , max;

    while( cin>>N ){
        max = 0;
        memset( C , 0 , sizeof( C ) );

        for( int i=1 ; i<=N ; i++ )    //下标改成从0开始,因为树状数组不是从0的开始的,否则管辖域为0;
            for( int j=1 ; j<=N ; j++ ){
                cin>>map[i][j];
                add( i , j , map[i][j] );
            }

        for( int i=1 ; i<=N ; i++ ){
            for( int j=i ; j<=N ; j++ ){
                for( int m=1 ; m<=N ; m++ )
                    s[m] = sum( m , j ) - sum( m , i-1 );    //求该段之和
                ans = dp( );
                if( ans>max )
                    max = ans;
            }
        }
        cout<<max<<endl;
    }

    return 0;
}

int dp( void ){
    int sum=0 , max=0;

    for( int i=0 ; i<N ; i++ ){    //和上一个函数一样
        sum += s[i];
        if( sum < 0 )
            sum = 0;
        if( sum > max )
            max = sum;
    }
    return max;
}

int  lowbit( int n ){
    return n & ( -n );    //管辖域就是n对应的二进制数从后往前第一个1所在位置是k(第一个位置是0),则管辖域就是2^k
}

void add( int k , int col , int n ){
    while( k <= N ){
        C[col][k] += n;    //累加更新
        k += lowbit( k );    //"父结点"
    }
}

int  sum( int col , int n ){
    int ss = 0;
    while( n ){
        ss += C[col][n];    //求和
        n -= lowbit( n );    //回到另一个点的管辖域范围内
    }
    return ss;
}

代码分析:由代码对比,很明显地看出树状数组的下标需要从1开始,从0开始是实现不了的(至少说不好实现),树状数组将求和的复杂度从O(n)降为了O(logn),但是得多开辅助存储空间C数组,这也就是经常遇到的消耗空间来节省时间(动态规划从一定意义上说就是部分深度优先搜索的节省消耗空间来节省时间的优化),对树状数组的发明者表示衷心地佩服!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值