用树状数组解决求区间最值的问题:hdu1754

    以前都学过树状数组,但是已经差不多忘记了!不过看一看后,马上就都回忆起来了!而且感觉经过这么久的学习,对树状数组有了更深一层的领悟!个人觉得树状数组在本质上与线段树是没有区别的!都是管理区间,只不过树状数组管理区间的方式不一样罢了!

    经过了解后就学习着解决hdu1754那道水题吧!我在网上看到的代码多半是用线段树解决的!这里把用色树状数组的解决代码贴出来吧!

70975862012-11-06 16:32:30Accepted1754593MS2620K1524 BC++xiaoshuai
 
 
  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include<algorithm> 
  4. #define M 200005 
  5. using namespace std; 
  6. int a[M]; 
  7. int Max[M]; 
  8. int hash[M]; 
  9. int n,m; 
  10. int lowbit(int x){ 
  11.     return x&(-x); 
  12. void build(int pos,int val){ 
  13.     while(pos<=n){ 
  14.         if(hash[pos]==0){ 
  15.             Max[pos]=val; 
  16.             hash[pos]++; 
  17.         }else
  18.             Max[pos]=max(Max[pos],val); 
  19.         } 
  20.         pos+=lowbit(pos); 
  21.     } 
  22. void update(int pos,int val){ 
  23.     //修改这元素 影响到的空间的值 
  24.     int i,j,k; 
  25.     a[pos]=val;//修改原始数组的值 
  26.     for(i=pos;i<=n;i+=lowbit(i)){ 
  27.         Max[i]=a[i]; 
  28.         if(lowbit(i)>1){ 
  29.           for(j=i-1,k=i-lowbit(i)+1;j>=k;j-=lowbit(j)){ 
  30.              Max[i]=max(Max[i],Max[j]); 
  31.           } 
  32.         } 
  33.     } 
  34. int query(int ll,int rr){ 
  35.     int ans=a[rr]; 
  36.     while(true){ 
  37.         ans=max(ans,a[rr]); 
  38.         if(ll==rr)break
  39.         for(rr-=1;rr-ll>=lowbit(rr);rr-=lowbit(rr)){ 
  40.             ans=max(ans,Max[rr]); 
  41.         } 
  42.     } 
  43.     return ans; 
  44. int main(){ 
  45.     int i; 
  46.     char s[2]; 
  47.     int ll,rr; 
  48.     int ans; 
  49.     while(scanf("%d %d",&n,&m)!=EOF){ 
  50.         memset(hash,0,sizeof(hash)); 
  51.         for(i=1;i<=n;i++){ 
  52.             scanf("%d",&a[i]); 
  53.             build(i,a[i]); 
  54.         } 
  55.         for(i=0;i<m;i++){ 
  56.             scanf("%s %d %d",s,&ll,&rr); 
  57.             if(s[0]=='U'){ 
  58.                 update(ll,rr); 
  59.             }else
  60.                 ans=query(ll,rr); 
  61.                 printf("%d\n",ans); 
  62.             } 
  63.         } 
  64.     } 
  65.     return 0; 

    尽管这道题是如此简单,但是依然花了我大半天的时间!看来自己的代码能力还是需要提高啊!查询的方法是借鉴了(博客现在找不到了)!把原始数据加进去操作和更新的操作是我自己写的! 说实话,看懂与自己拍出来的差距真的挺大的!

   特别是加入数据的操作,最开始学习树状数组是用电子科大的PPT里面的例子!

就是区间求和:

 
 
  1. #include<stdio.h> 
  2. #include<string.h> 
  3. int n=5; 
  4. int a[6]={0,1,2,3,4,5}; 
  5. int sum[6]; 
  6. int lowbit(int t){ 
  7.     return t&-t; 
  8. void update(int pos,int val){ 
  9.     while(pos<=n){ 
  10.         sum[pos]+=val; 
  11.         pos+=lowbit(pos); 
  12.     } 
  13. int query(int pos){ 
  14.     int ans=0; 
  15.     while(pos>0){ 
  16.         ans+=sum[pos]; 
  17.         pos-=lowbit(pos); 
  18.     } 
  19.     return ans; 
  20. int main(){ 
  21.     memset(sum,0,sizeof(sum)); 
  22.     for(int i=1;i<=n;i++){ 
  23.         update(i,a[i]); 
  24.     } 
  25.     int ans=query(5); 
  26.     printf("ans=%d\n",ans); 
  27.     return 0; 

   当时我的思路也仅限于 用数状数组来求和! 学习线段树的时候,求和与求最大值几乎是没有区别的!但是用树状数组我就一下子转不过来了!转不过来的时候,一定要弄清楚的是sum数组中存储的到底是什么?sum[i]表示i管理的区间之和!i管理的区间是哪一段呢?[i-lowbit(i}+1,i] 这个区间自己画一画就出来了!

    好了,回到就最值的问题!这里我为什么会用到hash呢?主要是对Max数组的初始化问题,我想如果有负数出现,而把Max数组初始化为0或者-1肯定是不对的!怎么办呢?用hash吧!只要位置i是第一次访问,Max[i]肯定与val是相等的!这样就解决了初始化数据的问题!

   真正卡了我好久的地方是update(int pos,int val),就是如果要修改一个值,我们怎么办?

这里就一定要理解清楚树状数组是怎么管理区间的!因为把pos位置的value修改后,Max数组可能是需要改变的!改变什么呢?修改pos位置影响到的区间!比如:pos=8,则Max[8],Max[16]的值都可能发生变化!我当时就是在修改区间的位置一直纠缠着!Max[8]=max(a[8],Max[7],Max[6],Max[4])!  那么怎么按顺序遍历8,7,6,4呢?用到lowbit()!

   经过一次又一次的wrong answer,一次又一次的反思,最终得到的update代码如下:

 
 
  1. void update(int pos,int val){ 
  2.     int i,j,k; 
  3.     a[pos]=val;//修改原始数组的值 
  4.     for(i=pos;i<=n;i+=lowbit(i)){ //修改这元素 影响到的空间的值 
  5.         Max[i]=a[i];//由于a[i]的值已经变化,所以Max[i]的值可能不再是原来的值,需要我们重新求 
  6.         if(lowbit(i)>1){//这里k=i-lowbit(i)+1,表示下标i管理的区间的起点即i下标管理区间[k,i] 
  7.           for(j=i-1,k=i-lowbit(i)+1;j>=k;j-=lowbit(j)){ 
  8.              Max[i]=max(Max[i],Max[j]); 
  9.           } 
  10.         } 
  11.     } 

    学习算法是一个悟的过程!所谓“觉悟”,觉是一个瞬间,悟是一个过程。把所有的觉的瞬间,与长长一生的悟结合起来,你所到达的就是终于看见我的心。这是人生的的觉悟。

   慢慢接着悟吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值