sicily 1136(线段树+最大子数组)
来源:互联网 发布:java模板引擎 编辑:程序博客网 时间:2024/05/16 03:29
题目链接: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(线段树+最大子数组)
- sicily 1136 山海经【线段树】
- Algorithm学习笔记 --- 最大子段和(线段树)
- hdu 3564 线段树+dp(最大递增子序列)
- 求区间最大子段和(线段树)
- Sicily 1136 山海经 (SOJ 1136) 【Segment Tree 线段树】
- sicily 1802 线段树
- 子数组的最大和(数组)
- 最大子数组和(最大子序列和 | 连续子数组最大和)
- 最大子数组和(最大子序列和 | 连续子数组最大和)(转)
- 最大子数组和(最大子段和)
- 最大子数组和(最大子段和)
- Codeforces 629 D Finals in arithmetic(最大上升子序列和,O(nlogn)、线段树/树状数组)
- sicily 1800 线段树RMQ
- 查找最大子串(后缀数组)
- 最大子数组(分治法)
- Maximum Subarray(最大子数组)
- 最大子数组之和(动态规划)
- RTX腾讯通怎么删除群聊人员
- C++成员变量的初始化顺序问题
- ubantu14.04 32位下第一个hello world驱动程序
- Win8.1 下 VS2010、VS2012中 扩展管理器 visual Assist X 启用,中文乱码 解决方案
- 堆排序
- sicily 1136(线段树+最大子数组)
- Qt基本特性
- KNN算法详解
- Struts2.0国际化
- Cocos2d-JS屏幕适配(NO-BORDER模式)
- 保持边缘的滤波函数————Guided Image Filtering
- 7、缓存、不可变类
- 第3周项目找对象--三角形类
- iOS开发中 常用的SQLite函数