码迷,mamicode.com
首页 > 编程语言 > 详细

sicily 1136(线段树+最大子数组)

时间:2015-03-19 22:10:55      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:

题目链接:sicily 1136

解题思路:
要求区间内的最大子数组,而且访问可能很频繁,时间复杂度需要达到o(n*logn),于是就很自然地想到了线段树。我们可以用线段树来保存区间的最大子数组,但是仔细想想又不对劲了,如果访问的区间跨越了两个小区间怎么破,所以,这并不只是一个简单的求区间连续和的问题,还要有点小技巧。
最大子数组怎么得到的,还记得《算法导论》里面讲过一种用分治法来求最大子数组的方法吗(分治法之最大子数组)?
假设我们要求区间[low , high]的最大子数组,并且已知[low , mid]和[mid+1 , high]的各种信息(mid为区间中点,这里的各种信息并未确定),我们要怎么得到最大子数组呢?于是有公式:
root->max_sum=max( root->l->max_sum , root->r->max_sum , root->l->lmax+root->r->rmax )
这里的lmax表示包含区间右边界的最大连续和,rmax则是包含左边界的最大连续和,由于题目最终需要求出区间,所以各种子数组的边界都需要记录,下面是线段树结构体的定义:

struct node{
    int left,right,sum;        //区间左、右边界,区间和
    long long lmax,rmax;       //包含右、左边界的最大连续和
    int l_index,r_index;       //lmax的左界,rmax的右界
    long long max_sum;         //最大连续和
    int left_bound,right_bound;     //最大连续和的左、右边界
    node *l,*r;
    node(int l,int r):left(l),right(r),l(NULL),r(NULL){}
};

然后lmax和rmax也有公式:
root->lmax=max( root->r->lmax , root->l->lmax+root->r->sum )
root->rmax=max( root->l->rmax , root->r->rmax+root->l->sum )
最后还有一个细节,就是答案的区间[i , j]的 i , j 得最小,就没什么大问题了!

AC代码:

#include <bits/stdc++.h>

using namespace std;

int n,a[100005];

struct node{
    int left,right,sum;
    long long lmax,rmax;
    int l_index,r_index;
    long long max_sum;
    int left_bound,right_bound;
    node *l,*r;
    node(int l,int r):left(l),right(r),l(NULL),r(NULL){}
};

void push_up(node* root){
    root->sum=root->l->sum+root->r->sum;

    root->lmax=root->r->lmax,root->l_index=root->r->l_index;    //from mid to left 
    if(root->l->lmax+root->r->sum >= root->lmax)
    {
        root->lmax=root->l->lmax+root->r->sum;
        root->l_index=root->l->l_index;
    }

    root->rmax=root->l->rmax,root->r_index=root->l->r_index;    //from mid to right
    if(root->r->rmax+root->l->sum > root->rmax)
    {
        root->rmax=root->r->rmax+root->l->sum;
        root->r_index=root->r->r_index;
    }

    root->max_sum=root->l->max_sum;                 //max sum
    root->left_bound=root->l->left_bound;       
    root->right_bound=root->l->right_bound;

    if(root->l->lmax+root->r->rmax > root->max_sum)
    {
        root->max_sum=root->l->lmax+root->r->rmax;
        root->left_bound=root->l->l_index;
        root->right_bound=root->r->r_index;
    }
    if(root->r->max_sum > root->max_sum)
    {
        root->max_sum=root->r->max_sum;
        root->left_bound=root->r->left_bound;
        root->right_bound=root->r->right_bound;
    }
}

void create(node* root)
{
    int l=root->left,r=root->right;
    if(l<r)
    {
        int mid=(l+r)>>1;
        root->l=new node(l,mid);
        root->r=new node(mid+1,r);
        create(root->l);
        create(root->r);
        push_up(root);
    }
    else
    {
        root->sum=a[l];
        root->lmax=root->rmax=root->sum;
        root->left_bound=root->right_bound=
                root->l_index=root->r_index=l;
        root->max_sum=root->sum;        
    }
}

node query(node* root,int l,int r)
{
    if(root->left==l&&root->right==r)
        return *root;

    int mid=(root->left + root->right)/2;
    if(mid>=r)
        return query(root->l,l,r);
    else if(l>mid)  
        return query(root->r,l,r);
    else
    {
        node ans_l=query(root->l,l,mid),
            ans_r=query(root->r,mid+1,r);

        node ans(l,r);
        ans.sum=ans_l.sum+ans_r.sum;

        ans.lmax=ans_r.lmax,ans.l_index=ans_r.l_index;      //from mid to left
        if(ans_l.lmax+ans_r.sum >= ans.lmax)
            ans.lmax=ans_l.lmax+ans_r.sum,ans.l_index=ans_l.l_index;

        ans.rmax=ans_l.rmax,ans.r_index=ans_l.r_index;      //from mid to right
        if(ans_r.rmax+ans_l.sum > ans.rmax)
            ans.rmax=ans_r.rmax+ans_l.sum,ans.r_index=ans_r.r_index;

        ans.max_sum=ans_l.max_sum,                          //max sum
            ans.left_bound=ans_l.left_bound,
                ans.right_bound=ans_l.right_bound;
        if(ans_l.lmax+ans_r.rmax > ans.max_sum)
        {
            ans.max_sum=ans_l.lmax+ans_r.rmax;
            ans.left_bound=ans_l.l_index,ans.right_bound=ans_r.r_index;
        }
        if(ans_r.max_sum > ans.max_sum)
            ans.max_sum=ans_r.max_sum,
                ans.left_bound=ans_r.left_bound,
                    ans.right_bound=ans_r.right_bound;

        return ans;
    } 
}

void print(node* root)//测试用
{
    printf("l:%d r:%d sum:%d\n",root->left,root->right,root->sum);
    printf("mid_to_left:%d :%d\n",root->l_index,root->lmax);
    printf("mid_to_right:%d :%d\n",root->r_index,root->rmax);
    printf("max: l:%d r:%d sum:%d\n\n",root->left_bound,root->right_bound,root->max_sum);
    if(root->l)
        print(root->l);
    if(root->r)
        print(root->r);
}

int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);

    node* root=new node(1,n);
    create(root);
//  print(root);

    int x,y;
    while(m--)
    {
        scanf("%d %d",&x,&y);
        node tmp=query(root,x,y);
        printf("%d %d %lld\n",tmp.left_bound,tmp.right_bound,tmp.max_sum);
    }

    return 0;
}

总结:
1、线段树真是个好东西,跟区间有关的查询,多考虑它;
2、分治法求最大子数组的方法也是挺重要的;
3、注重细节,千万不可手贱!!!

sicily 1136(线段树+最大子数组)

标签:

原文地址:http://blog.csdn.net/fuyukai/article/details/44463211

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!