现在的位置: 首页 > 综合 > 正文

poj 3264 Balanced Lineup(rmq vs 线段树)

2012年01月10日 ⁄ 综合 ⁄ 共 4046字 ⁄ 字号 评论关闭

题目链接:http://poj.org/problem?id=3264

第一次做rmq问题,据说这道题是最简单的rmq问题了。。。题意很简单了,就是给一组数据,随即的求出某个区间内最大数和最小数之差。

查了许多资料,首先rmq原理:用A[1..N]表示一组数,F[I,J]表示从A[I]到A[I+2^J-1]这个范围内的最大值,也就是以A[I]为起点连续2^J个数的最大值,由于元素个数为2^J个,所以从中间平均分成两部分,每一部分的元素个数刚好为2^(J-1)个,整个区间的最大值一定是左右两部分最大值的较大值,满足动态规划的最优原理状态转移方程:F[I,J]=max(F[I,J-1],F[I+2^(J-1),J-1]),边界条件为F[I,0]=A[I],伪代码:

for(int i=1;i<=n;i++) 
   f[i,0]:=a[i];

for(int j=1;j<=ln(n)/ln(2);j++)
{
   for(int i=1;i<=n+1-1<<j;j++) 
     f[i,j]=max(f[i,j-1],f[i+1<<(j-1),j-1]);  
};

道理很简单,╮(╯▽╰)╭。。。不知道我的代码到底问题出哪里了,第二组数据怎么也过不去,,上网查了各路大神的代码??求指导,求排错。。。。。下边是我的有问题的代码

View Code

#include <stdio.h>
#include <math.h>
const int MAXNUM = 50005;
int N,Q;//数字个数,和问题个数
int num[MAXNUM];//存放数字的数组
int dp_max[MAXNUM][20];//存放区间最大值
int dp_min[MAXNUM][20];//存放区间最小值

//返回较大值
int get_max(int a,int b)
{
    return a>b?a:b;
}
//返回较小值
int get_min(int a,int b)
{
    return a>b?b:a;
}
//rmq算法的初始化,预处理
void rmq_init()
{
    //int k = (int)log((double)(N+1))/log(2.0);
    for(int i = 1; i <= N; i++)
    dp_max[i][0] = dp_min[i][0] = num[i];

    for(int j = 1; (1<<j) <= N; j++)
    {
        for(int i = 1; i+(1<<j)-1 <= N; i++)
        {
            int t = i+(1<<(j-1));
            dp_max[i][j] = get_max(dp_max[i][j-1],dp_max[t][j-1]);
            dp_min[i][j] = get_min(dp_min[i][j-1],dp_min[t][j-1]);
        }
    }

}

//rmq查询区间[a,b]最大值和最小值之差
int query(int a,int b)
{
    int min,max;//最大值最小值
    int k = (int)(log((b-a+1)*1.0)/log(2.0));
    int t = b-(1<<k)+1;
    min = get_min(dp_min[a][k],dp_min[t][k]);
    max = get_max(dp_max[a][k],dp_min[t][k]);
    return max-min;
}

int main()
{
    int a,b;
    scanf("%d%d",&N,&Q);
    for(int i = 1; i <= N; i++)
    scanf("%d",&num[i]);
    rmq_init();
    for(int i = 0; i < Q; i++)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",query(a,b));
    }
    return 0;
}

 

这时网上找到的的代码,http://www.cnblogs.com/cnjy/archive/2009/08/30/1556566.html

View Code

#include <iostream>
#include <string>
#include <math.h>
using namespace std;

#define maxs( a , b ) a>b?a:b
#define mins( a , b ) a>b?b:a
const int MAX_N = 50005;

int d[MAX_N];
int dpmin[MAX_N][20];
int dpmax[MAX_N][20];
int n;

void create_Dpmin(){
     int i , j;
     for( i = 1 ; i <= n ; i++ )
          dpmin[i][0] = d[i];
     for( j = 1 ; j <= log((double)(n+1))/log(2.0) ; j++ ){
          for( i = 1 ; i+(1<<j)-1 <= n ; i++ ){
               dpmin[i][j] = mins( dpmin[i][j-1] , dpmin[i+(1<<(j-1))][j-1] );    
          }
     }     
}
void create_Dpmax(){
     int i , j;
     for( i = 1 ; i <= n ; i++ )
          dpmax[i][0] = d[i];
     for( j = 1 ; j <= log((double)(n+1))/log(2.0) ; j++ ){
          for( i = 1 ; i+(1<<j)-1 <= n ; i++ ){
               dpmax[i][j] = maxs( dpmax[i][j-1] , dpmax[i+(1<<(j-1))][j-1] );    
          }
     }     
}

int getmax( int a , int b ){
    int k = (int)(log((double)(b-a+1))/log(2.0));
    return maxs( dpmax[a][k] , dpmax[b-(1<<k)+1][k] );    
}

int getmin( int a , int b ){
    int k = (int)(log((double)(b-a+1))/log(2.0));
    return mins( dpmin[a][k] , dpmin[b-(1<<k)+1][k] );    
}
void Init()
{
     create_Dpmin();
     create_Dpmax();     
}

int main()
{   
    freopen( "in.txt" , "r" , stdin );
    int i , m , a , b;
    scanf("%d%d",&n,&m);
    for( i = 1 ; i <= n ; i++ ){
           scanf("%d",&d[i]); 
    } 
    Init();
    while( m-- ){
           scanf("%d%d",&a,&b);     
           printf("%d\n",getmax(a,b)-getmin(a,b));  
    }   
    return 0;    
}

 

rmq问题的标准解法是O(nlogn)的预处理, O(1)的查询。不过线段树可以实现O(nlogn)的预处理,O(logn)的查询,速度也不错。下边是用线段树写的,效率没有rmq快。
 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

const int MAXNUM = 50005;
int N,Q;//数字个数,和问题个数
int num[MAXNUM];//数据
int maxn,minn;

typedef struct Node
{
    int ld,rd;//左边界、右边界
    Node *lc,*rc;//左孩子,右孩子
    int nmax,nmin;//最大值、最小值
}Node;

Node *root;

//返回较大值
int maxs(int a,int b)
{
    return a>b?a:b;
}
//返回较小值
int mins(int a,int b)
{
    return a>b?b:a;
}
//刚开始这个函数一直内存错误,头疼,调了半天,
//当给孩子节点分配内存时是在递归中分配的,要想使用通过返回值就可以了,
//不然一直内存错误
Node *build_tree(Node *node,int l,int r)
{
    node = (Node *)malloc(sizeof(Node));
    node->ld = l;
    node->rd = r;
    if(l!=r)
    {
        int mid = (l+r)>>1;
        node->lc = build_tree(node->lc,l,mid);
        node->rc = build_tree(node->rc,mid+1,r);
        node->nmax = maxs(node->lc->nmax,node->rc->nmax);
        node->nmin = mins(node->lc->nmin,node->rc->nmin);
    }else
    {
        node->nmax = node->nmin = num[l];
        node->lc = node->rc = NULL;
    }
    return node;
}

void query(Node *node,int l,int r)
{
    //注意判断节点如果为null就直接返回了
    if(node==NULL)return;
    //相当于一个剪枝,如果最小值已经小于这个节点所存储的区间的最小值,
    //且最大值已经小于这个区间的最大值时就没有必要再向下搜索的必要了
    if(minn <= node->nmin && maxn >= node->nmax)
        return;
    if(node->ld == l && node->rd == r)
    {
        maxn = maxs(node->nmax,maxn);
        minn = mins(node->nmin,minn);
    }
    int mid = (node->ld+node->rd)>>1;
    if(mid>r)
    query(node->lc,l,r);
    else if(mid<l)
    query(node->rc,l,r);
    else
    {
        query(node->lc,l,mid);
        query(node->rc,mid+1,r);
    }

}

int main()
{
    int l,r;
    scanf("%d%d",&N,&Q);
    for(int i = 1; i <= N; i++)
    scanf("%d",&num[i]);
    //root->lc = root->rc = NULL;
    root = build_tree(root,1,N);
    while(Q--)
    {
        scanf("%d%d",&l,&r);
        maxn = -1;
        minn = 1000005;
        query(root,l,r);
        printf("%d\n",maxn-minn);
    }
    return 0;
}

 

【上篇】
【下篇】

抱歉!评论已关闭.