HDU 3607 线段树+DP+离散化

题意:从左往右跳箱子,每个箱子有金币数量,只能从矮处向高处跳,求最大可获得金币数,数据规模1<=n<=1e5.

显然是一个dp的问题,不难得出dp[ i ] = max(dp[j] )+val [ i ] ,j < i ; 第一眼会想到o(n^2)的算法,显然会超时,这个时候就需要用线段树维护最大值,将复杂度降低到o(nlogn)

首先离散化处理,将高度从小到大排序,并使用unique函数去重,之后每个高度就可以映射为它的下标pos,然后用线段树维护每个下标对应的最优解bestans [ pos ] ,每当向后进行新的决策时,

先找出状态转移方程中的max( dp [ j ] ) ,线段树操作是o(logn)的,然后用dp [ i ] = max( dp [ j ])+val[ i ] 更新线段树中 ( h[ i ] 对应下标pos ) bestans[ pos ] 的值

#include<bits/stdc++.h>
#define N 100050
#define debug cout<<"???"<<endl;
using namespace std;
int val[N],mx[N<<2],h[N];
void pushup(int rt)
{
    mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
int query(int L,int R,int l,int r,int rt)
{
    if(L>R)return 0;
    if(L<=l&&r<=R)return mx[rt];
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m)ans=max(ans,query(L,R,l,m,rt<<1));
    if(R>m)ans=max(ans,query(L,R,m+1,r,rt<<1|1));
    return ans;
}
void updata(int pos,int val,int l,int r,int rt)
{
    if(l==r){
        mx[rt]=val;
        return;
    }
    int m=(l+r)>>1;
    if(pos<=m)updata(pos,val,l,m,rt<<1);
    if(pos>m)updata(pos,val,m+1,r,rt<<1|1);
    pushup(rt);
}
void init()
{
    memset(mx,0, sizeof(mx));
}
int main()
{
    int n,x;
    while(cin>>n) {
        init();
        vector<int>ve;
        for (int i = 1; i <= n; i++) {
            scanf("%d%d",&h[i],val+i);
            ve.emplace_back(h[i]);
        }
        int ans=0;
        sort(ve.begin(),ve.end());
        unique(ve.begin(),ve.end());
        for(int i=1;i<=n;i++)
        {
            int pos=int(lower_bound(ve.begin(),ve.end(),h[i])-ve.begin()+1);
            int tmp=query(1,pos-1,1,int(ve.size()),1);
            updata(pos,tmp+val[i],1,int(ve.size()),1);
            ans=max(ans,tmp+val[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xusirui/p/9396145.html