HDU 6447 YJJ's Salesman (线段树优化DP)

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define ll long long

const int  maxn =1e5+5;
const int mod=1e9+7;

/*
题目大意:给定一些坐标点,每个坐标点都有权重。。
然后每次选择都必须选择斜下方,不能横着或竖着走这样。
求走到最后收集的最大值。

赛场上没有敏感度,,看到坐标范围开到九次方思维被带偏。。
其实可以线段树离散化处理下的。
如果坐标范围是五次方级别,那么这题思维就简单点了。

首先明显是个简单的DP问题,
假设现在在i点,如何使得到达i点权重最大呢?
找之前的比它纵坐标小的最大值相加即可。
然而这题如果权重都是1则可以直接二分处理
(dp答案筛选空间如果是单调则直接二分查找符合条件的最大值,
如果不是二分,则树状结构维护极值)。

但权重各异,需要线段树维护,一个很典型的类型了,
这里还是采用贡献思维来讲解,对点集的y坐标建树,
每个y坐标的贡献是它的DP值,对当前点的查询就查询1到当前点y坐标-1的最大DP值。

当然这里会想到有个小麻烦的地方,x坐标和y坐标可能有个重复的集合。
对于这种情况,把y坐标压缩处理后,先对x坐标排序,
如果x坐标一样,对y倒着排序,这样顺序遍历时就可以保证在同一x值下,
y值更新的答案互不影响,不然比如:
1 1 1
1 2 2
者这两个点,在检索答案时第二个点可能最优解检索到第一个点上了,但是x坐标一样不符题意,
如果从上往下检索就不影响了。
*/

int n;
///节点结构
struct node
{
    int x,y,v;///横纵坐标和价值
};
node seq[maxn];
bool cmp1(node p,node q)
{
    return p.y<q.y;
}
bool cmp2(node p,node q)
{
    if(p.x!=q.x) return p.x<q.x;
    return p.y>q.y;
}

int i,tot;

///线段树结构
int maxv[maxn<<2];
void pushup(lrt) { maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]); }
void build(lrt)
{
    maxv[rt]=0;if(l==r) return ;
    int mid=l+r>>1;
    build(lson),build(rson),pushup(root);
}
void update(lrt,int pos,int v)
{
    if(l==r){maxv[rt]=max(maxv[rt],v);return; }
    int mid=l+r>>1;
    if(pos<=mid) update(lson,pos,v);
    if(mid<pos) update(rson,pos,v);
    pushup(root);
}
int query(lrt,int L,int R)
{
    if(L<=l&&r<=R) return maxv[rt];
    int mid=l+r>>1,ans=0;
    if(L<=mid) ans=max(ans,query(lson,L,R));
    if(mid<R) ans=max(ans,query(rson,L,R));
    return ans;
}

int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);for(int i=0;i<n;i++) scanf("%d%d%d",&seq[i].x,&seq[i].y,&seq[i].v);

        sort(seq,seq+n,cmp1);

        i=0,tot=0;
        while(i<n)
        {
            int v=seq[i].y;
            tot++;
            while(i<n && seq[i].y==v)
            {
                seq[i].y=tot;
                i++;
            }
        }///更新为相对高度


        sort(seq,seq+n,cmp2);

        build(1,maxn,1);
        i=0;
        while(i<n)
        {
            int tp=seq[i].x;
            ///更新位置seq[i].x
            while(i<n && seq[i].x==tp)
            {
                int mv;
                if(seq[i].y-1<1) mv=0;
                else mv=query(1,maxn,1,1,seq[i].y-1);///查询最大值
                update(1,maxn,1,seq[i].y,mv+seq[i].v);///更新那个高度的最大值
                i++;
            }
        }
        printf("%d\n",maxv[1]);
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值